[packages] 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


git-svn-id: svn://svn.openwrt.org/openwrt/packages@23388 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
craigc
2010-10-10 20:59:38 +00:00
parent 8b9c1c5804
commit c6f4b884fc
4 changed files with 294 additions and 295 deletions

View File

@ -8,7 +8,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=multiwan PKG_NAME:=multiwan
PKG_VERSION:=1.0.18 PKG_VERSION:=1.0.19
PKG_RELEASE:=1 PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/package.mk

View File

@ -1,11 +1,18 @@
config 'multiwan' 'config' config 'multiwan' 'config'
option 'default_route' 'balancer' 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' config 'interface' 'wan'
option 'weight' '10' option 'weight' '10'
option 'health_interval' '10' option 'health_interval' '10'
option 'icmp_hosts' 'dns' 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 'timeout' '3'
option 'health_fail_retries' '3' option 'health_fail_retries' '3'
option 'health_recovery_retries' '5' option 'health_recovery_retries' '5'
@ -23,19 +30,25 @@ config 'interface' 'wan2'
option 'dns' '208.67.222.222 208.67.220.220' option 'dns' '208.67.222.222 208.67.220.220'
config 'mwanfw' config 'mwanfw'
option 'src' '192.168.1.0/24' option 'src' '192.168.1.0/24'
option 'dst' 'ftp.netlab7.com' option 'dst' 'ftp.netlab7.com'
option 'proto' 'tcp' option 'proto' 'tcp'
option 'ports' '21' option 'ports' '21'
option 'wanrule' 'wan2' 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' config 'mwanfw'
option 'src' '192.168.0.3' option 'src' '192.168.0.3'
option 'proto' 'icmp' option 'proto' 'icmp'
option 'wanrule' 'balancer' option 'wanrule' 'balancer'
config 'mwanfw' config 'mwanfw'
option 'dst' 'www.whatismyip.com' option 'dst' 'www.whatismyip.com'
option 'wanrule' 'fastbalancer' option 'wanrule' 'fastbalancer'

View File

@ -1,8 +1,9 @@
#!/bin/sh /etc/rc.common #!/bin/sh /etc/rc.common
START=99 START=99
EXTRA_COMMANDS="single"
start () { start () {
sh /usr/bin/multiwan agent /usr/bin/multiwan agent &
} }
stop () { stop () {
@ -10,6 +11,10 @@ stop () {
} }
restart () { restart () {
sh /usr/bin/multiwan restart /usr/bin/multiwan restart &
}
single () {
/usr/bin/multiwan single &
} }

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/sh
. /etc/functions.sh . /etc/functions.sh
@ -15,26 +15,19 @@ mwnote() {
} }
failover() { failover() {
local failover_to local failchk=$(query_config failchk $2)
local failover_to_wanid local recvrychk=$(query_config recvrychk $2)
local failchk
local recovrychk
local wanid
local existing_failover
failchk=$(query_config failchk $2) local wanid=$(query_config wanid $2)
recvrychk=$(query_config recvrychk $2) local failover_to=$(uci_get_state multiwan ${2} failover_to)
local failover_to_wanid=$(query_config wanid $failover_to)
wanid=$(query_config wanid $2) local existing_failover=$(iptables -n -L FW${wanid}MARK -t mangle | echo $(($(wc -l) - 2)))
failover_to=`uci -q -P /var/state get multiwan.${2}.failover_to`
failover_to_wanid=$(query_config wanid $failover_to)
existing_failover=$(iptables -n -L FW${wanid}MARK -t mangle | echo $(expr $(wc -l) - 2))
add() { add() {
wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g") 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") wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
update_cache update_cache
@ -69,57 +62,44 @@ failover() {
} }
fail_wan() { fail_wan() {
local failchk
local recvrychk
local new_fail_count local new_fail_count
local health_fail_retries
local weight
health_fail_retries=`uci -q -P /var/state get multiwan.${1}.health_fail_retries` local health_fail_retries=$(uci_get_state multiwan ${1} health_fail_retries)
weight=`uci -q -P /var/state get multiwan.${1}.weight` local weight=$(uci_get_state multiwan ${1} weight)
failchk=$(query_config failchk $1) local failchk=$(query_config failchk $1)
recvrychk=$(query_config recvrychk $1) local recvrychk=$(query_config recvrychk $1)
wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g") wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
if [ -z "$failchk" ]; then if [ -z "$failchk" ]; then
failchk=1
wan_fail_map="$wan_fail_map${1}[1]" wan_fail_map="$wan_fail_map${1}[1]"
update_cache fi
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
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
fi fi
update_cache
} }
recover_wan() { recover_wan() {
local failchk
local recvrychk
local new_fail_count 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` local health_recovery_retries=$(uci_get_state multiwan ${1} health_recovery_retries)
weight=`uci -q -P /var/state get multiwan.${1}.weight` local weight=$(uci_get_state multiwan ${1} weight)
failchk=$(query_config failchk $1) local failchk=$(query_config failchk $1)
recvrychk=$(query_config recvrychk $1) local recvrychk=$(query_config recvrychk $1)
wanid=$(query_config wanid $1) local wanid=$(query_config wanid $1)
if [ ! -z "$failchk" -a "$failchk" != "x" ]; then if [ ! -z "$failchk" -a "$failchk" != "x" ]; then
wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g") wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g")
@ -134,7 +114,7 @@ recover_wan() {
recover_wan $1 recover_wan $1
fi fi
else else
new_recovery_count=$(expr $recvrychk + 1) new_recovery_count=$(($recvrychk + 1))
if [ "$new_recovery_count" -lt "$health_recovery_retries" ]; then 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") wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]/$1\[${new_recovery_count}\]/g")
update_cache update_cache
@ -150,33 +130,20 @@ recover_wan() {
} }
acquire_wan_data() { acquire_wan_data() {
local ipaddr
local gateway
local ifname
local check_old_map local check_old_map
local get_wanid local get_wanid
local old_ifname local old_ifname
local old_ipaddr local old_ipaddr
local old_gateway local old_gateway
ifname=`uci -q -P /var/state get network.${1}.ifname` local ifname=$(uci_get_state network ${1} ifname 'x')
ipaddr=`uci -q -P /var/state get network.${1}.ipaddr` local ipaddr=$(uci_get_state network ${1} ipaddr 'x')
gateway=`uci -q -P /var/state get network.${1}.gateway` local gateway=$(uci_get_state network ${1} gateway 'x')
if [ -z "$ifname" ]; then check_old_map=$(echo $wan_id_map 2>&1 | grep -o "$1\[")
ifname="x"
fi
if [ -z "$ipaddr" ]; then
ipaddr="x"
fi
if [ -z "$gateway" ]; then
gateway="x"
fi
check_old_map=`echo $wan_id_map 2>&1 | grep -o "$1\["`
if [ -z $check_old_map ]; then if [ -z $check_old_map ]; then
wancount=`expr $wancount + 1` wancount=$(($wancount + 1))
if [ $wancount -gt 20 ]; then if [ $wancount -gt 20 ]; then
wancount=20 wancount=20
return return
@ -217,7 +184,6 @@ acquire_wan_data() {
} }
update_cache() { update_cache() {
if [ ! -d /tmp/.mwan ]; then if [ ! -d /tmp/.mwan ]; then
mkdir /tmp/.mwan > /dev/null 2>&1 mkdir /tmp/.mwan > /dev/null 2>&1
fi fi
@ -232,17 +198,18 @@ update_cache() {
echo "wan_gw_map=\"$wan_gw_map\"" >> /tmp/.mwan/cache echo "wan_gw_map=\"$wan_gw_map\"" >> /tmp/.mwan/cache
echo "wan_fail_map=\"$wan_fail_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_recovery_map=\"$wan_recovery_map\"" >> /tmp/.mwan/cache
echo "wan_monitor_map=\"$wan_monitor_map\"" >> /tmp/.mwan/cache
} }
query_config() { query_config() {
case $1 in case $1 in
update) update_cache_data;;
ifname) echo $wan_if_map | grep -o "$2\[\w*.*\]" | awk -F "[" '{print $2}' | awk -F "]" '{print $1}';; 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}';; 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}';; 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}';; 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}';; 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}';; 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}';; group) echo $wan_id_map | grep -o "\w*\[$2\]" | awk -F "[" '{print $1}';;
esac esac
} }
@ -253,43 +220,42 @@ mwan_kill() {
sleep 2 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() { stop() {
local group
local i
mwan_kill 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 ##" echo "## Refreshing Interfaces ##"
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1` local group=$(query_config group $i)
group=$(query_config group $i) ifup $group >&- 2>&- && sleep 1
# ifdown $group > /dev/null 2>&1
ifup $group > /dev/null 2>&1
done done
echo "## Unloaded, updating syslog and exiting. ##" echo "## Unloaded, updating syslog and exiting. ##"
mwnote "Succesfully Unloaded on $(exec date -R)." mwnote "Succesfully Unloaded on $(exec date -R)."
ip route flush cache rm -fr /tmp/.mwan >&- 2>&-
rm -r /tmp/.mwan > /dev/null 2>&1 fi
ip route flush cache
else
if [ "$1" == "restart" ]; then
echo "## Restarting Multi-WAN. ##" echo "## Restarting Multi-WAN. ##"
mwnote "Reinitializing Multi-WAN Configuration." mwnote "Reinitializing Multi-WAN Configuration."
ip route flush cache rm -fr /tmp/.mwan >&- 2>&-
rm -r /tmp/.mwan > /dev/null 2>&1 /etc/init.d/multiwan start >&- 2>&-
/etc/init.d/multiwan start & > /dev/null 2>&1
fi fi
exit exit
} }
clear_rules() { clear_rules() {
local restore_single=$1
local group local group
local i
iptables -t mangle -F PREROUTING iptables -t mangle -F PREROUTING
iptables -t mangle -F FORWARD iptables -t mangle -F FORWARD
@ -309,35 +275,31 @@ clear_rules() {
iptables -t mangle -X LoadBalancer iptables -t mangle -X LoadBalancer
iptables -t mangle -F FastBalancer iptables -t mangle -F FastBalancer
iptables -t mangle -X FastBalancer iptables -t mangle -X FastBalancer
iptables -t mangle -F MultiWanLoadBalancer
iptables -t mangle -X MultiWanLoadBalancer
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
iptables -t mangle -F FW${i}MARK 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 iptables -t mangle -X FW${i}MARK
done done
if [ ! -z "$CHKFORQOS" ]; then if [ ! -z "$CHKFORQOS" ]; then
iptables -t mangle -F MultiWanQoS iptables -t mangle -F MultiWanQoS
iptables -t mangle -X MultiWanQoS iptables -t mangle -X MultiWanQoS
i=0 i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
iptables -t mangle -F qos_${group} iptables -t mangle -F qos_${group}
iptables -t mangle -F qos_${group}_ct iptables -t mangle -F qos_${group}_ct
iptables -t mangle -X qos_${group} iptables -t mangle -X qos_${group}
iptables -t mangle -X qos_${group}_ct iptables -t mangle -X qos_${group}_ct
done done
fi fi
[ "$restore_single" == 'single' ] &&
/etc/init.d/qos restart > /dev/null 2>&1
} }
qos_init() { qos_init() {
@ -350,8 +312,6 @@ qos_init() {
local execute local execute
local iprule local iprule
local qos_if_test local qos_if_test
local i
local p
ifname=$(query_config ifname $1) ifname=$(query_config ifname $1)
@ -373,7 +333,7 @@ qos_init() {
return return
fi fi
queue_count=`expr $queue_count + 1` queue_count=$(($queue_count + 1))
iptables -t mangle -N qos_${1} iptables -t mangle -N qos_${1}
iptables -t mangle -N qos_${1}_ct 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") 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 while [ $i -lt $queue_count ]; do
echo "s/\(0x$i \|0x$i\/0xffffffff\)/0x$(expr $2 \* 10 + $i) /g" >> /tmp/.mwan/qos.$1.sedfilter echo "s/\(0x$i \|0x$i\/0xffffffff\)/0x$(($2 * 10 + $i)) /g" >> /tmp/.mwan/qos.$1.sedfilter
i=`expr $i + 1` i=$(($i + 1))
done done
add_qos_iptables=$(echo "$get_wan_iptables" | sed -f /tmp/.mwan/qos.$1.sedfilter) add_qos_iptables=$(echo "$get_wan_iptables" | sed -f /tmp/.mwan/qos.$1.sedfilter)
@ -395,7 +355,7 @@ qos_init() {
i=1 i=1
while [ $i -lt $queue_count ]; do while [ $i -lt $queue_count ]; do
echo "s/0x$i /0x${2}${i} fw /g" >> /tmp/.mwan/qos.$1.sedfilter echo "s/0x$i /0x${2}${i} fw /g" >> /tmp/.mwan/qos.$1.sedfilter
i=`expr $i + 1` i=$(($i + 1))
done done
add_qos_tc=$(echo "$get_wan_tc" | sed -f /tmp/.mwan/qos.$1.sedfilter) add_qos_tc=$(echo "$get_wan_tc" | sed -f /tmp/.mwan/qos.$1.sedfilter)
@ -404,11 +364,11 @@ qos_init() {
i=0 i=0
while [ $i -lt $queue_count ]; do while [ $i -lt $queue_count ]; do
if [ $i -lt $(expr $queue_count - 1) ]; then if [ $i -lt $(($queue_count - 1)) ]; then
ip rule add fwmark 0x$(expr $2 \* 10 + $i + 1) table $(expr $2 + 170) prio $(expr $2 \* 10 + $i + 2) ip rule add fwmark 0x$(($2 * 10 + $i + 1)) table $(($2 + 170)) prio $(( $2 * 10 + $i + 2))
fi fi
iptables -t mangle -A MultiWanQoS -m mark --mark 0x$(expr $2 \* 10 + $i) -j qos_${1} iptables -t mangle -A MultiWanQoS -m mark --mark 0x$(($2 * 10 + $i)) -j qos_${1}
i=`expr $i + 1` i=$(($i + 1))
done done
} }
@ -454,10 +414,8 @@ mwanrule() {
if [ "$src" == "all" ]; then if [ "$src" == "all" ]; then
src=$NULL src=$NULL
fi fi
iptables -t mangle -A MultiWanRules -m mark --mark 0x0\ iptables -t mangle -A MultiWanRules ${src:+-s $src} ${dst:+-d $dst} \
${proto:+-p $proto} \ -m mark --mark 0x0 ${proto:+-p $proto -m $proto} \
${src:+-s $src} \
${dst:+-d $dst} \
${ports:+-m multiport --$port_type $ports} \ ${ports:+-m multiport --$port_type $ports} \
-j $wanrule -j $wanrule
} }
@ -480,7 +438,6 @@ refresh_dns() {
local failchk local failchk
local compile_dns local compile_dns
local dns_server local dns_server
local i
iptables -F MultiWanDNS -t mangle iptables -F MultiWanDNS -t mangle
@ -489,21 +446,16 @@ refresh_dns() {
echo "## Refreshing DNS Resolution and Tables ##" echo "## Refreshing DNS Resolution and Tables ##"
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
gateway=$(query_config gateway $group) gateway=$(query_config gateway $group)
ipaddr=$(query_config ipaddr $group) ipaddr=$(query_config ipaddr $group)
ifname=$(query_config ifname $group) ifname=$(query_config ifname $group)
failchk=$(query_config failchk $group) failchk=$(query_config failchk $group)
dns=`uci -q -P /var/state get multiwan.${group}.dns` dns=$(uci_get_state multiwan ${group} dns 'auto')
[ "$dns" == "auto" ] && dns=$(uci_get_state network ${group} dns)
if [ -z "$dns" -o "$dns" == "auto" ]; then
dns=`uci -q -P /var/state get network.${group}.dns`
fi
dns=$(echo $dns | sed -e "s/ /\n/g") dns=$(echo $dns | sed -e "s/ /\n/g")
if [ ! -z "$dns" -a "$failchk" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" -a "$ifname" != "x" ]; then 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 /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 PREROUTING
iptables -t mangle -F FORWARD iptables -t mangle -F FORWARD
@ -549,8 +501,7 @@ iptables_init() {
iptables -t mangle -N MultiWanQoS iptables -t mangle -N MultiWanQoS
i=0 i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
qos_init $(query_config group $i) $i qos_init $(query_config group $i) $i
done done
@ -567,9 +518,8 @@ iptables_init() {
echo "## Creating FW Rules ##" echo "## Creating FW Rules ##"
i=0 i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1` iprule=$(($i * 10))
iprule=$(expr $i \* 10)
iptables -t mangle -N FW${i}MARK 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 MARK --set-mark 0x${iprule}
iptables -t mangle -A FW${i}MARK -j CONNMARK --save-mark iptables -t mangle -A FW${i}MARK -j CONNMARK --save-mark
@ -615,8 +565,7 @@ iptables_init() {
fi fi
i=0 i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
ifname=$(query_config ifname $group) ifname=$(query_config ifname $group)
iptables -t mangle -A MultiWanPreHandler -i $ifname -m state --state NEW -j FW${i}MARK 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 if [ ! -z "$CHKFORQOS" ]; then
iptables -t mangle -A MultiWan -j MultiWanQoS iptables -t mangle -A MultiWan -j MultiWanQoS
fi fi
} }
refresh_loadbalancer() { refresh_loadbalancer() {
@ -638,8 +586,6 @@ refresh_loadbalancer() {
local nexthop local nexthop
local pre_nexthop_chk local pre_nexthop_chk
local rand_probability local rand_probability
local total_weight
local i
echo "## Refreshing Load Balancer ##" echo "## Refreshing Load Balancer ##"
@ -654,36 +600,34 @@ refresh_loadbalancer() {
iptables -F MultiWanLoadBalancer -t mangle iptables -F MultiWanLoadBalancer -t mangle
total_weight=0 local total_weight=0
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
failchk=$(query_config failchk $group) failchk=$(query_config failchk $group)
gateway=$(query_config gateway $group) gateway=$(query_config gateway $group)
ifname=$(query_config ifname $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 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 fi
done done
i=0 i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
failchk=$(query_config failchk $group) failchk=$(query_config failchk $group)
gateway=$(query_config gateway $group) gateway=$(query_config gateway $group)
ifname=$(query_config ifname $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 if [ "$gateway" != "x" -a "$ifname" != "x" -a "$failchk" != "x" -a "$weight" != "disable" ]; then
nexthop="$nexthop nexthop via $gateway dev $ifname weight $weight" nexthop="$nexthop nexthop via $gateway dev $ifname weight $weight"
rand_probability=$(expr $(expr $weight \* 100) / $total_weight) rand_probability=$(($weight * 100 / $total_weight))
total_weight=$(expr $total_weight - $weight) total_weight=$(($total_weight - $weight))
if [ $rand_probability -lt 10 ]; then if [ $rand_probability -lt 10 ]; then
rand_probability="0.0${rand_probability}" rand_probability="0.0${rand_probability}"
@ -700,7 +644,7 @@ refresh_loadbalancer() {
done 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 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 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 elif [ "$pre_nexthop_chk" -gt "1" ]; then
@ -717,27 +661,24 @@ refresh_routes() {
local group local group
local ifname local ifname
local ipaddr local ipaddr
local i
echo "## Refreshing Routing Tables ##" echo "## Refreshing Routing Tables ##"
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
gateway=$(query_config gateway $group) gateway=$(query_config gateway $group)
ifname=$(query_config ifname $group) ifname=$(query_config ifname $group)
ipaddr=$(query_config ipaddr $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 TABLE=$(($i + 170))
ip route | grep -Ev ^default | while read ROUTE; do ip route | grep -Ev ^default | while read ROUTE; do
ip route add table $TABLE to $ROUTE ip route add table $TABLE to $ROUTE
done
done done
if [ "$gateway" != "x" -a "$ipaddr" != "x" -a "$ifname" != "x" ]; then 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 route add default gw $gateway > /dev/null 2>&1
fi fi
done done
@ -746,7 +687,6 @@ refresh_routes() {
} }
iprules_config() { iprules_config() {
local iprule local iprule
local group local group
local gateway local gateway
@ -756,23 +696,22 @@ iprules_config() {
gateway=$(query_config gateway $group) gateway=$(query_config gateway $group)
ipaddr=$(query_config ipaddr $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 if [ -z "$CHKIPROUTE" ]; then
echo "$(expr $1 + 170) MWAN${1}" >> /etc/iproute2/rt_tables echo "$(($1 + 170)) MWAN${1}" >> /etc/iproute2/rt_tables
fi fi
ip rule del prio $(expr $1 \* 10) > /dev/null 2>&1 ip rule del prio $(($1 * 10)) > /dev/null 2>&1
ip rule del prio $(expr $1 \* 10 + 1) > /dev/null 2>&1 ip rule del prio $(($1 * 10 + 1)) > /dev/null 2>&1
if [ "$gateway" != "x" -a "$ipaddr" != "x" ]; then if [ "$gateway" != "x" -a "$ipaddr" != "x" ]; then
ip rule add from $ipaddr table $(expr $1 + 170) prio $(expr $1 \* 10) ip rule add from $ipaddr table $(($1 + 170)) prio $(($1 * 10))
ip rule add fwmark 0x$(expr $1 \* 10) table $(expr $1 + 170) prio $(expr $(expr $1 \* 10) + 1) ip rule add fwmark 0x$(($1 * 10)) table $(($1 + 170)) prio $(($1 * 10 + 1))
fi fi
} }
flush() { flush() {
local i local restore_single=$1
echo "## Flushing IP Rules & Routes ##" echo "## Flushing IP Rules & Routes ##"
ip rule flush > /dev/null 2>&1 ip rule flush > /dev/null 2>&1
@ -781,24 +720,22 @@ flush() {
ip route flush table 170 > /dev/null ip route flush table 170 > /dev/null
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
ip route del default > /dev/null 2>&1 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 done
echo "## Clearing Rules ##" echo "## Clearing Rules ##"
clear_rules > /dev/null 2>&1 clear_rules $restore_single > /dev/null 2>&1
rm $jobfile > /dev/null 2>&1 rm $jobfile > /dev/null 2>&1
} }
main_init() { main_init() {
local RP_PATH local RP_PATH IFACE
local group local group
local health_interval local health_interval
local i
echo "## Main Initialization ##" echo "## Main Initialization ##"
@ -807,18 +744,16 @@ main_init() {
mwan_kill mwan_kill
flush flush
echo "## IP Rules Initialization ##" echo "## IP Rules Initialization ##"
CHKIPROUTE=`cat /etc/iproute2/rt_tables | grep LoadBalancer` CHKIPROUTE=$(grep LoadBalancer /etc/iproute2/rt_tables)
if [ -z "$CHKIPROUTE" ]; then if [ -z "$CHKIPROUTE" ]; then
echo "#" >> /etc/iproute2/rt_tables echo "#" >> /etc/iproute2/rt_tables
echo "170 LoadBalancer" >> /etc/iproute2/rt_tables echo "170 LoadBalancer" >> /etc/iproute2/rt_tables
fi fi
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
iprules_config $i iprules_config $i
done done
@ -828,89 +763,49 @@ main_init() {
refresh_loadbalancer refresh_loadbalancer
RP_PATH=/proc/sys/net/ipv4/conf 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 echo 0 > $RP_PATH/$IFACE/rp_filter
done done
echo "## Initialization Complete, switching to background mode. ##" mwnote "Succesfully Initialized on $(date -R)."
mwnote "Succesfully Initialized on $(exec date -R)."
fail_start_check fail_start_check
stagger_health_monitors() { while :; do
i=0 schedule_tasks
while [ $i -lt $wancount ]; do do_tasks
i=`expr $i + 1` done
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
} }
health_monitor() { monitor_wan() {
local ipaddr_cur local ifname ipaddr gateway icmp_hosts_acquire icmp_test_host
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
local check_test local check_test
local health_interval
local check_for_job
. /tmp/.mwan/cache . /tmp/.mwan/cache
timeout=`uci -q -P /var/state get multiwan.${1}.timeout` local timeout=$(uci_get_state multiwan ${1} timeout)
icmp_hosts=`uci -q -P /var/state get multiwan.${1}.icmp_hosts` local icmp_hosts=$(uci_get_state multiwan ${1} icmp_hosts)
health_interval=`uci -q -P /var/state get multiwan.${1}.health_interval` local icmp_count=$(uci_get_state multiwan ${1} icmp_count '1')
ifname_cur=$(query_config ifname $1) local health_interval=$(uci_get_state multiwan ${1} health_interval)
ipaddr_cur=$(query_config ipaddr $1) local ifname_cur=$(query_config ifname $1)
gateway_cur=$(query_config gateway $1) local ipaddr_cur=$(query_config ipaddr $1)
local gateway_cur=$(query_config gateway $1)
while [ 1 ]; do while :; do
[ "${health_monitor%.*}" = 'parallel' ] && sleep $health_interval
ifname=`uci -q -P /var/state get network.${1}.ifname` ifname=$(uci_get_state network ${1} ifname 'x')
ipaddr=`uci -q -P /var/state get network.${1}.ipaddr` ipaddr=$(uci_get_state network ${1} ipaddr 'x')
gateway=`uci -q -P /var/state get network.${1}.gateway` gateway=$(uci_get_state network ${1} gateway 'x')
if [ -z "$ifname" ]; then
ifname="x"
fi
if [ -z "$ipaddr" ]; then
ipaddr="x"
fi
if [ -z "$gateway" ]; then
gateway="x"
fi
if [ "$ifname_cur" != "$ifname" -o "$ipaddr_cur" != "$ipaddr" -o "$gateway_cur" != "$gateway" ]; then if [ "$ifname_cur" != "$ifname" -o "$ipaddr_cur" != "$ipaddr" -o "$gateway_cur" != "$gateway" ]; then
echo $1.acquire >> $jobfile add_task "$1" acquire
exit if [ "${health_monitor%.*}" = 'parallel' ]; then
else exit
if [ "$gateway" != "x" ]; then else
default_routes_check=`ip route | grep -o $gateway` return
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
fi fi
else
[ "$gateway" != "x" ] && ! ip route | grep -o $gateway >&- 2>&- &&
add_task route refresh
fi fi
if [ "$icmp_hosts" != "disable" -a "$ifname" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" ]; then 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 if [ "$icmp_hosts" == "gateway" -o -z "$icmp_hosts" ]; then
icmp_hosts_acquire=$gateway icmp_hosts_acquire=$gateway
elif [ "$icmp_hosts" == "dns" ]; then elif [ "$icmp_hosts" == "dns" ]; then
icmp_hosts_acquire=`uci -q -P /var/state get multiwan.$1.dns` icmp_hosts_acquire=$(uci_get_state multiwan $1 dns 'auto')
if [ -z "$icmp_hosts_acquire" -o "$icmp_hosts_acquire" == "auto" ]; then [ "$icmp_hosts_acquire" == "auto" ] &&
icmp_hosts_acquire=`uci -q -P /var/state get network.$1.dns` icmp_hosts_acquire=$(uci_get_state network $1 dns)
fi
else else
icmp_hosts_acquire=$icmp_hosts icmp_hosts_acquire=$icmp_hosts
fi fi
@ -930,45 +824,119 @@ health_monitor() {
ping_test() { ping_test() {
echo "$icmp_hosts" | while read icmp_test_host; do 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 done
} }
check_test=$(ping_test) check_test=$(ping_test)
if [ -z "$check_test" ]; then if [ -z "$check_test" ]; then
echo "$1.fail" >> $jobfile add_task "$1" fail
else else
echo "$1.pass" >> $jobfile add_task "$1" pass
fi fi
elif [ "$icmp_hosts" == "disable" ]; then elif [ "$icmp_hosts" == "disable" ]; then
echo "$1.pass" >> $jobfile add_task "$1" pass
fi 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 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 check_iptables
local queued_task local queued_task
local bg_counter
local current_resolv_file local current_resolv_file
bg_counter=0 while :; do
while [ 1 ]; do
. /tmp/.mwan/cache . /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) 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 if [ -z "$check_iptables" -o "$check_iptables" -lt 4 ]; then
mwnote "Netfilter rules appear to of been altered." mwnote "Netfilter rules appear to of been altered."
/etc/init.d/multiwan restart & /etc/init.d/multiwan restart
exit exit
fi fi
@ -978,8 +946,7 @@ bg_task() {
refresh_dns refresh_dns
fi fi
bg_counter=0 rule_counter=0
fi fi
if [ -f $jobfile ]; then if [ -f $jobfile ]; then
@ -992,21 +959,31 @@ bg_task() {
case $2 in case $2 in
fail) fail_wan $1;; fail) fail_wan $1;;
pass) recover_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;; refresh) refresh_routes;;
*) echo "## Unknown task command: $2 ##";;
esac esac
} }
queued_task=`echo $LINE | awk -F "." '{print $1,$2}'` queued_task=$(echo $LINE | awk -F "." '{print $1,$2}')
execute_task $queued_task execute_task $queued_task
done < $jobfile.work done < $jobfile.work
rm $jobfile.work rm $jobfile.work
fi fi
bg_counter=$(expr $bg_counter + 1) if [ "$health_monitor" = 'serial' ]; then
break
sleep 1 else
sleep 1
fi
done done
} }
@ -1016,9 +993,8 @@ fail_start_check(){
local ifname local ifname
local group local group
i=0 local i=0
while [ $i -lt $wancount ]; do while [ $((i++)) -lt $wancount ]; do
i=`expr $i + 1`
group=$(query_config group $i) group=$(query_config group $i)
ifname=$(query_config ifname $group) ifname=$(query_config ifname $group)
ipaddr=$(query_config ipaddr $group) ipaddr=$(query_config ipaddr $group)
@ -1031,24 +1007,29 @@ fail_start_check(){
} }
wancount=0 wancount=0
max_interval=$(((1<<31) - 1))
config_clear config_clear
config_load "multiwan" config_load "multiwan"
config_get default_route config default_route config_get default_route config default_route
config_get debug config debug 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 config_foreach acquire_wan_data interface
update_cache update_cache
CHKFORQOS=`iptables -n -L Default -t mangle 2>&1 | grep "Chain Default"` CHKFORQOS=$(iptables -n -L Default -t mangle 2>&1 | grep "Chain Default")
CHKFORMODULE=`iptables -m statistic 2>&1 | grep -o "File not found"` CHKFORMODULE=$(iptables -m statistic 2>&1 | grep -o "File not found")
jobfile="/tmp/.mwan/jobqueue" jobfile="/tmp/.mwan/jobqueue"
case $1 in case $1 in
agent) silencer main_init;; agent) silencer main_init;;
restart) silencer stop restart;;
stop) silencer stop;; stop) silencer stop;;
restart) silencer stop restart;;
single) silencer stop single;;
esac esac