c6f4b884fc
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
1036 lines
28 KiB
Bash
Executable File
1036 lines
28 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
. /etc/functions.sh
|
|
|
|
silencer() {
|
|
if [ -z "$debug" -o "$debug" == "0" ]; then
|
|
$* > /dev/null 2>&1
|
|
else
|
|
$*
|
|
fi
|
|
}
|
|
|
|
mwnote() {
|
|
logger ${debug:+-s} -p 5 -t multiwan "$1"
|
|
}
|
|
|
|
failover() {
|
|
local failchk=$(query_config failchk $2)
|
|
local recvrychk=$(query_config recvrychk $2)
|
|
|
|
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)
|
|
|
|
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="$wan_fail_map${1}[x]"
|
|
wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
|
|
update_cache
|
|
|
|
if [ "$existing_failover" == "2" ]; then
|
|
if [ "$failover_to" != "balancer" -a "$failover_to" != "fastbalancer" -a "$failover_to" != "disable" -a "$failover_to_wanid" != "$wanid" ]; then
|
|
iptables -I FW${wanid}MARK 2 -t mangle -j FW${failover_to_wanid}MARK
|
|
elif [ "$failover_to" == "balancer" ]; then
|
|
iptables -I FW${wanid}MARK 2 -t mangle -j LoadBalancer
|
|
elif [ "$failover_to" == "fastbalancer" ]; then
|
|
iptables -I FW${wanid}MARK 2 -t mangle -j FastBalancer
|
|
fi
|
|
fi
|
|
mwnote "$1 has failed and is currently offline."
|
|
}
|
|
|
|
del() {
|
|
|
|
wan_recovery_map=$(echo $wan_recovery_map | sed -e "s/${1}\[${recvrychk}\]//g")
|
|
wan_fail_map=$(echo $wan_fail_map | sed -e "s/${1}\[${failchk}\]//g")
|
|
update_cache
|
|
|
|
if [ "$existing_failover" == "3" ]; then
|
|
iptables -D FW${wanid}MARK 2 -t mangle
|
|
fi
|
|
mwnote "$1 has recovered and is back online!"
|
|
}
|
|
|
|
case $1 in
|
|
add) add $2;;
|
|
del) del $2;;
|
|
esac
|
|
}
|
|
|
|
fail_wan() {
|
|
local new_fail_count
|
|
|
|
local health_fail_retries=$(uci_get_state multiwan ${1} health_fail_retries)
|
|
local weight=$(uci_get_state multiwan ${1} weight)
|
|
|
|
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]"
|
|
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 new_fail_count
|
|
|
|
local health_recovery_retries=$(uci_get_state multiwan ${1} health_recovery_retries)
|
|
local weight=$(uci_get_state multiwan ${1} weight)
|
|
|
|
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")
|
|
update_cache
|
|
fi
|
|
|
|
if [ "$failchk" == "x" ]; then
|
|
if [ -z "$recvrychk" ]; then
|
|
wan_recovery_map="$wan_recovery_map${1}[1]"
|
|
update_cache
|
|
if [ "$health_recovery_retries" == "1" ]; then
|
|
recover_wan $1
|
|
fi
|
|
else
|
|
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
|
|
else
|
|
failover del $1
|
|
refresh_dns
|
|
if [ "$weight" != "disable" ]; then
|
|
refresh_loadbalancer
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
acquire_wan_data() {
|
|
local check_old_map
|
|
local get_wanid
|
|
local old_ifname
|
|
local old_ipaddr
|
|
local old_gateway
|
|
|
|
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\[")
|
|
|
|
if [ -z $check_old_map ]; then
|
|
wancount=$(($wancount + 1))
|
|
if [ $wancount -gt 20 ]; then
|
|
wancount=20
|
|
return
|
|
fi
|
|
wan_if_map="$wan_if_map${1}[${ifname}]"
|
|
wan_id_map="$wan_id_map${1}[${wancount}]"
|
|
wan_gw_map="$wan_gw_map${1}[${gateway}]"
|
|
wan_ip_map="$wan_ip_map${1}[${ipaddr}]"
|
|
else
|
|
old_ipaddr=$(query_config ipaddr $1)
|
|
old_gateway=$(query_config gateway $1)
|
|
old_ifname=$(query_config ifname $1)
|
|
get_wanid=$(query_config wanid $1)
|
|
|
|
wan_if_map=$(echo $wan_if_map | sed -e "s/${1}\[${old_ifname}\]/$1\[${ifname}\]/g")
|
|
wan_ip_map=$(echo $wan_ip_map | sed -e "s/${1}\[${old_ipaddr}\]/$1\[${ipaddr}\]/g")
|
|
wan_gw_map=$(echo $wan_gw_map | sed -e "s/${1}\[${old_gateway}\]/$1\[${gateway}\]/g")
|
|
|
|
if [ "$old_ifname" != "$ifname" ]; then
|
|
iptables -D MultiWanPreHandler -t mangle -i $old_$ifname -m state --state NEW -j FW${get_wanid}MARK
|
|
iptables -A MultiWanPreHandler -t mangle -i $ifname -m state --state NEW -j FW${get_wanid}MARK
|
|
iptables -D MultiWanPostHandler -t mangle -o $old_$ifname -m mark --mark 0x1 -j FW${get_wanid}MARK
|
|
iptables -A MultiWanPostHandler -t mangle -o $ifname -m mark --mark 0x1 -j FW${get_wanid}MARK
|
|
fi
|
|
|
|
if [ "$ifname" != "x" -a "$ipaddr" != "x" -a "$gateway" != "x" ]; then
|
|
failover del $1
|
|
iprules_config $get_wanid
|
|
else
|
|
failover add $1
|
|
fi
|
|
|
|
refresh_routes
|
|
refresh_loadbalancer
|
|
refresh_dns
|
|
update_cache
|
|
fi
|
|
}
|
|
|
|
update_cache() {
|
|
if [ ! -d /tmp/.mwan ]; then
|
|
mkdir /tmp/.mwan > /dev/null 2>&1
|
|
fi
|
|
|
|
rm /tmp/.mwan/cache > /dev/null 2>&1
|
|
touch /tmp/.mwan/cache
|
|
|
|
echo "# Automatically Generated by Multi-WAN Agent Script. Do not modify or remove. #" > /tmp/.mwan/cache
|
|
echo "wan_id_map=\"$wan_id_map\"" >> /tmp/.mwan/cache
|
|
echo "wan_if_map=\"$wan_if_map\"" >> /tmp/.mwan/cache
|
|
echo "wan_ip_map=\"$wan_ip_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_recovery_map=\"$wan_recovery_map\"" >> /tmp/.mwan/cache
|
|
echo "wan_monitor_map=\"$wan_monitor_map\"" >> /tmp/.mwan/cache
|
|
}
|
|
|
|
query_config() {
|
|
case $1 in
|
|
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
|
|
}
|
|
|
|
mwan_kill() {
|
|
local otherpids=$(ps 2>&1 | grep 'multiwan agent' | grep -v $$ | awk '{print $1}')
|
|
[ -n "$otherpids" ] && kill $otherpids > /dev/null 2>&1
|
|
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() {
|
|
mwan_kill
|
|
flush $1
|
|
|
|
if [ "$1" == "single" ]; then
|
|
# ifup is quite expensive--do it only when single wan is requested
|
|
echo "## Refreshing Interfaces ##"
|
|
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)."
|
|
rm -fr /tmp/.mwan >&- 2>&-
|
|
fi
|
|
ip route flush cache
|
|
|
|
if [ "$1" == "restart" ]; then
|
|
echo "## Restarting Multi-WAN. ##"
|
|
mwnote "Reinitializing Multi-WAN Configuration."
|
|
rm -fr /tmp/.mwan >&- 2>&-
|
|
/etc/init.d/multiwan start >&- 2>&-
|
|
fi
|
|
|
|
exit
|
|
}
|
|
|
|
clear_rules() {
|
|
local restore_single=$1
|
|
local group
|
|
|
|
iptables -t mangle -F PREROUTING
|
|
iptables -t mangle -F FORWARD
|
|
iptables -t mangle -F POSTROUTING
|
|
iptables -t mangle -F OUTPUT
|
|
iptables -t mangle -F MultiWan
|
|
iptables -t mangle -X MultiWan
|
|
iptables -t mangle -F MultiWanRules
|
|
iptables -t mangle -X MultiWanRules
|
|
iptables -t mangle -F MultiWanDNS
|
|
iptables -t mangle -X MultiWanDNS
|
|
iptables -t mangle -F MultiWanPreHandler
|
|
iptables -t mangle -X MultiWanPreHandler
|
|
iptables -t mangle -F MultiWanPostHandler
|
|
iptables -t mangle -X MultiWanPostHandler
|
|
iptables -t mangle -F LoadBalancer
|
|
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
|
|
|
|
local i=0
|
|
while [ $((i++)) -lt $wancount ]; do
|
|
iptables -t mangle -F FW${i}MARK
|
|
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
|
|
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() {
|
|
local ifname
|
|
local queue_count
|
|
local get_wan_tc
|
|
local get_wan_iptables
|
|
local add_qos_iptables
|
|
local add_qos_tc
|
|
local execute
|
|
local iprule
|
|
local qos_if_test
|
|
|
|
ifname=$(query_config ifname $1)
|
|
|
|
if [ "$ifname" == "x" ]; then
|
|
return
|
|
fi
|
|
|
|
qos_if_test=$(echo $qos_if_done | grep $ifname.)
|
|
|
|
if [ ! -z "$qos_if_test" ]; then
|
|
return
|
|
fi
|
|
|
|
qos_if_done=$(echo ${qos_if_done}.${ifname})
|
|
|
|
queue_count=$(tc filter list dev $ifname | tail -n 1 | awk -F " " '{print $10}' | sed "s/0x//g")
|
|
|
|
if [ -z "$queue_count" ]; then
|
|
return
|
|
fi
|
|
|
|
queue_count=$(($queue_count + 1))
|
|
|
|
iptables -t mangle -N qos_${1}
|
|
iptables -t mangle -N qos_${1}_ct
|
|
|
|
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")
|
|
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")
|
|
|
|
|
|
local i=0
|
|
while [ $i -lt $queue_count ]; do
|
|
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)
|
|
echo "$add_qos_iptables" | while read execute; do ${execute}; done
|
|
|
|
rm /tmp/.mwan/qos.$1.sedfilter
|
|
i=1
|
|
while [ $i -lt $queue_count ]; do
|
|
echo "s/0x$i /0x${2}${i} fw /g" >> /tmp/.mwan/qos.$1.sedfilter
|
|
i=$(($i + 1))
|
|
done
|
|
|
|
add_qos_tc=$(echo "$get_wan_tc" | sed -f /tmp/.mwan/qos.$1.sedfilter)
|
|
echo "$add_qos_tc" | while read execute; do ${execute}; done
|
|
rm /tmp/.mwan/qos.$1.sedfilter
|
|
|
|
i=0
|
|
while [ $i -lt $queue_count ]; do
|
|
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$(($2 * 10 + $i)) -j qos_${1}
|
|
i=$(($i + 1))
|
|
done
|
|
}
|
|
|
|
mwanrule() {
|
|
local src
|
|
local dst
|
|
local ports
|
|
local proto
|
|
local wanrule
|
|
|
|
config_get src $1 src
|
|
config_get dst $1 dst
|
|
config_get port_type $1 port_type 'dports'
|
|
config_get ports $1 ports
|
|
config_get proto $1 proto
|
|
config_get wanrule $1 wanrule
|
|
|
|
if [ -z "$wanrule" ]; then
|
|
return
|
|
fi
|
|
|
|
if [ "$wanrule" != "balancer" -a "$wanrule" != "fastbalancer" ]; then
|
|
wanrule=$(query_config wanid ${wanrule})
|
|
wanrule="FW${wanrule}MARK"
|
|
elif [ "$wanrule" == "balancer" ]; then
|
|
wanrule="LoadBalancer"
|
|
elif [ "$wanrule" == "fastbalancer" ]; then
|
|
wanrule="FastBalancer"
|
|
fi
|
|
if [ "$dst" == "all" ]; then
|
|
dst=$NULL
|
|
fi
|
|
if [ "$proto" == "all" ]; then
|
|
proto=$NULL
|
|
fi
|
|
if [ "$ports" == "all" ]; then
|
|
ports=$NULL
|
|
fi
|
|
add_rule() {
|
|
if [ "$proto" == "icmp" ]; then
|
|
ports=$NULL
|
|
fi
|
|
if [ "$src" == "all" ]; then
|
|
src=$NULL
|
|
fi
|
|
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
|
|
}
|
|
if [ -z "$proto" -a ! -z "$ports" ]; then
|
|
proto=tcp
|
|
add_rule
|
|
proto=udp
|
|
add_rule
|
|
return
|
|
fi
|
|
add_rule
|
|
}
|
|
|
|
refresh_dns() {
|
|
local dns
|
|
local group
|
|
local ipaddr
|
|
local gateway
|
|
local ifname
|
|
local failchk
|
|
local compile_dns
|
|
local dns_server
|
|
|
|
iptables -F MultiWanDNS -t mangle
|
|
|
|
rm /tmp/resolv.conf.auto
|
|
touch /tmp/resolv.conf.auto
|
|
|
|
echo "## Refreshing DNS Resolution and Tables ##"
|
|
|
|
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_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
|
|
echo "$dns" | while read dns_server; do
|
|
iptables -t mangle -A MultiWanDNS -d $dns_server -p tcp --dport 53 -j FW${i}MARK
|
|
iptables -t mangle -A MultiWanDNS -d $dns_server -p udp --dport 53 -j FW${i}MARK
|
|
|
|
compile_dns="nameserver $dns_server"
|
|
echo "$compile_dns" >> /tmp/resolv.conf.auto
|
|
done
|
|
fi
|
|
done
|
|
|
|
last_resolv_update=$(ls -l -e /tmp/resolv.conf.auto | awk -F " " '{print $5, $9}')
|
|
}
|
|
|
|
iptables_init() {
|
|
echo "## IPTables Rule Initialization ##"
|
|
local iprule
|
|
local group
|
|
local ifname
|
|
local execute
|
|
local IMQ_NFO
|
|
local default_route_id
|
|
local i
|
|
|
|
if [ ! -z "$CHKFORQOS" ]; then
|
|
echo "## QoS Initialization ##"
|
|
|
|
/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}')
|
|
|
|
iptables -t mangle -F PREROUTING
|
|
iptables -t mangle -F FORWARD
|
|
iptables -t mangle -F POSTROUTING
|
|
iptables -t mangle -F OUTPUT
|
|
|
|
echo "$IMQ_NFO" | while read execute; do
|
|
iptables -t mangle -A PREROUTING -i $(echo $execute | awk -F " " '{print $1}') -j IMQ --todev $(echo $execute | awk -F " " '{print $2}')
|
|
done
|
|
|
|
iptables -t mangle -N MultiWanQoS
|
|
|
|
i=0
|
|
while [ $((i++)) -lt $wancount ]; do
|
|
qos_init $(query_config group $i) $i
|
|
done
|
|
|
|
fi
|
|
|
|
iptables -t mangle -N MultiWan
|
|
iptables -t mangle -N LoadBalancer
|
|
iptables -t mangle -N FastBalancer
|
|
iptables -t mangle -N MultiWanRules
|
|
iptables -t mangle -N MultiWanDNS
|
|
iptables -t mangle -N MultiWanPreHandler
|
|
iptables -t mangle -N MultiWanPostHandler
|
|
iptables -t mangle -N MultiWanLoadBalancer
|
|
|
|
echo "## Creating FW Rules ##"
|
|
i=0
|
|
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
|
|
done
|
|
|
|
iptables -t mangle -A LoadBalancer -j MARK --set-mark 0x1
|
|
iptables -t mangle -A LoadBalancer -j CONNMARK --save-mark
|
|
|
|
if [ -z "$CHKFORMODULE" ]; then
|
|
iptables -t mangle -A FastBalancer -j MARK --set-mark 0x2
|
|
iptables -t mangle -A FastBalancer -j CONNMARK --save-mark
|
|
else
|
|
mwnote "Performance load balancer(fastbalanacer) is unavailable due to current kernel limitations."
|
|
iptables -t mangle -A FastBalancer -j MARK --set-mark 0x1
|
|
iptables -t mangle -A FastBalancer -j CONNMARK --save-mark
|
|
fi
|
|
|
|
iptables -t mangle -A MultiWan -j CONNMARK --restore-mark
|
|
iptables -t mangle -A MultiWan -j MultiWanPreHandler
|
|
iptables -t mangle -A MultiWan -j MultiWanRules
|
|
iptables -t mangle -A MultiWan -j MultiWanLoadBalancer
|
|
iptables -t mangle -A MultiWan -j MultiWanDNS
|
|
iptables -t mangle -A MultiWan -j MultiWanPostHandler
|
|
|
|
iptables -t mangle -I PREROUTING -j MultiWan
|
|
iptables -t mangle -I FORWARD -j MultiWan
|
|
iptables -t mangle -I OUTPUT -j MultiWan
|
|
iptables -t mangle -I POSTROUTING -j MultiWan
|
|
|
|
|
|
refresh_dns
|
|
|
|
config_load "multiwan"
|
|
config_foreach mwanrule mwanfw
|
|
|
|
if [ "$default_route" != "balancer" -a "$default_route" != "fastbalancer" ]; then
|
|
default_route_id=$(query_config wanid $default_route)
|
|
iptables -t mangle -A MultiWanRules -m mark --mark 0x0 -j FW${default_route_id}MARK
|
|
elif [ "$default_route" == "fastbalancer" ]; then
|
|
iptables -t mangle -A MultiWanRules -m mark --mark 0x0 -j FastBalancer
|
|
else
|
|
iptables -t mangle -A MultiWanRules -m mark --mark 0x0 -j LoadBalancer
|
|
fi
|
|
|
|
i=0
|
|
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
|
|
iptables -t mangle -A MultiWanPostHandler -o $ifname -m mark --mark 0x1 -j FW${i}MARK
|
|
done
|
|
|
|
if [ ! -z "$CHKFORQOS" ]; then
|
|
iptables -t mangle -A MultiWan -j MultiWanQoS
|
|
fi
|
|
}
|
|
|
|
refresh_loadbalancer() {
|
|
local group
|
|
local gateway
|
|
local ifname
|
|
local failchk
|
|
local weight
|
|
local nexthop
|
|
local pre_nexthop_chk
|
|
local rand_probability
|
|
|
|
echo "## Refreshing Load Balancer ##"
|
|
|
|
ip rule del prio 9 > /dev/null 2>&1
|
|
ip route flush table 170 > /dev/null 2>&1
|
|
|
|
for TABLE in 170; do
|
|
ip route | grep -Ev ^default | while read ROUTE; do
|
|
ip route add table $TABLE to $ROUTE
|
|
done
|
|
done
|
|
|
|
iptables -F MultiWanLoadBalancer -t mangle
|
|
|
|
local total_weight=0
|
|
|
|
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_get_state multiwan ${group} weight)
|
|
if [ "$gateway" != "x" -a "$ifname" != "x" -a "$failchk" != "x" -a "$weight" != "disable" ]; then
|
|
total_weight=$(($total_weight + $weight))
|
|
fi
|
|
done
|
|
|
|
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_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=$(($weight * 100 / $total_weight))
|
|
total_weight=$(($total_weight - $weight))
|
|
|
|
if [ $rand_probability -lt 10 ]; then
|
|
rand_probability="0.0${rand_probability}"
|
|
elif [ $rand_probability -lt 100 ]; then
|
|
rand_probability="0.${rand_probability}"
|
|
else
|
|
rand_probability="1.0"
|
|
fi
|
|
|
|
if [ -z "$CHKFORMODULE" ]; then
|
|
iptables -A MultiWanLoadBalancer -t mangle -m mark --mark 0x2 -m statistic --mode random --probability $rand_probability -j FW${i}MARK
|
|
fi
|
|
fi
|
|
|
|
done
|
|
|
|
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
|
|
ip route add proto static table 170 default scope global $nexthop
|
|
fi
|
|
|
|
ip rule add fwmark 0x1 table 170 prio 9
|
|
ip route flush cache
|
|
}
|
|
|
|
refresh_routes() {
|
|
local iprule
|
|
local gateway
|
|
local group
|
|
local ifname
|
|
local ipaddr
|
|
|
|
echo "## Refreshing Routing Tables ##"
|
|
|
|
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 $(($i + 170)) > /dev/null 2>&1
|
|
|
|
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 $(($i + 170)) src $ipaddr proto static
|
|
route add default gw $gateway > /dev/null 2>&1
|
|
fi
|
|
done
|
|
|
|
ip route flush cache
|
|
}
|
|
|
|
iprules_config() {
|
|
local iprule
|
|
local group
|
|
local gateway
|
|
local ipaddr
|
|
|
|
group=$(query_config group $1)
|
|
gateway=$(query_config gateway $group)
|
|
ipaddr=$(query_config ipaddr $group)
|
|
|
|
CHKIPROUTE=$(grep MWAN${1} /etc/iproute2/rt_tables)
|
|
if [ -z "$CHKIPROUTE" ]; then
|
|
echo "$(($1 + 170)) MWAN${1}" >> /etc/iproute2/rt_tables
|
|
fi
|
|
|
|
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 $(($1 + 170)) prio $(($1 * 10))
|
|
ip rule add fwmark 0x$(($1 * 10)) table $(($1 + 170)) prio $(($1 * 10 + 1))
|
|
fi
|
|
}
|
|
|
|
flush() {
|
|
local restore_single=$1
|
|
echo "## Flushing IP Rules & Routes ##"
|
|
|
|
ip rule flush > /dev/null 2>&1
|
|
ip rule add lookup main prio 32766 > /dev/null 2>&1
|
|
ip rule add lookup default prio 32767 > /dev/null 2>&1
|
|
|
|
ip route flush table 170 > /dev/null
|
|
|
|
local i=0
|
|
while [ $((i++)) -lt $wancount ]; do
|
|
ip route del default > /dev/null 2>&1
|
|
ip route flush table $(($i + 170)) > /dev/null 2>&1
|
|
done
|
|
|
|
echo "## Clearing Rules ##"
|
|
clear_rules $restore_single > /dev/null 2>&1
|
|
|
|
rm $jobfile > /dev/null 2>&1
|
|
}
|
|
|
|
main_init() {
|
|
local RP_PATH IFACE
|
|
local group
|
|
local health_interval
|
|
|
|
echo "## Main Initialization ##"
|
|
|
|
mkdir /tmp/.mwan > /dev/null 2>&1
|
|
|
|
mwan_kill
|
|
flush
|
|
|
|
echo "## IP Rules Initialization ##"
|
|
|
|
CHKIPROUTE=$(grep LoadBalancer /etc/iproute2/rt_tables)
|
|
if [ -z "$CHKIPROUTE" ]; then
|
|
echo "#" >> /etc/iproute2/rt_tables
|
|
echo "170 LoadBalancer" >> /etc/iproute2/rt_tables
|
|
fi
|
|
|
|
local i=0
|
|
while [ $((i++)) -lt $wancount ]; do
|
|
iprules_config $i
|
|
done
|
|
|
|
refresh_routes
|
|
iptables_init
|
|
|
|
refresh_loadbalancer
|
|
|
|
RP_PATH=/proc/sys/net/ipv4/conf
|
|
for IFACE in $(ls $RP_PATH); do
|
|
echo 0 > $RP_PATH/$IFACE/rp_filter
|
|
done
|
|
mwnote "Succesfully Initialized on $(date -R)."
|
|
fail_start_check
|
|
|
|
while :; do
|
|
schedule_tasks
|
|
do_tasks
|
|
done
|
|
}
|
|
|
|
monitor_wan() {
|
|
local ifname ipaddr gateway icmp_hosts_acquire icmp_test_host
|
|
local check_test
|
|
|
|
. /tmp/.mwan/cache
|
|
|
|
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)
|
|
|
|
while :; do
|
|
[ "${health_monitor%.*}" = 'parallel' ] && sleep $health_interval
|
|
|
|
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
|
|
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
|
|
|
|
if [ "$icmp_hosts" == "gateway" -o -z "$icmp_hosts" ]; then
|
|
icmp_hosts_acquire=$gateway
|
|
elif [ "$icmp_hosts" == "dns" ]; then
|
|
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
|
|
|
|
icmp_hosts=$(echo $icmp_hosts_acquire | sed -e "s/\,/ /g" | sed -e "s/ /\n/g")
|
|
|
|
ping_test() {
|
|
echo "$icmp_hosts" | while read icmp_test_host; do
|
|
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
|
|
add_task "$1" fail
|
|
else
|
|
add_task "$1" pass
|
|
fi
|
|
|
|
elif [ "$icmp_hosts" == "disable" ]; then
|
|
add_task "$1" pass
|
|
fi
|
|
|
|
[ "$health_monitor" = 'serial' ] && {
|
|
wan_monitor_map=$(echo $wan_monitor_map | sed -e "s/$1\[\w*\]/$1\[$(date +%s)\]/g")
|
|
update_cache
|
|
break
|
|
}
|
|
done
|
|
}
|
|
|
|
# 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 current_resolv_file
|
|
|
|
while :; do
|
|
|
|
. /tmp/.mwan/cache
|
|
|
|
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
|
|
exit
|
|
fi
|
|
|
|
current_resolv_file=$(ls -l -e /tmp/resolv.conf.auto | awk -F " " '{print $5, $9}')
|
|
|
|
if [ "$last_resolv_update" != "$current_resolv_file" ]; then
|
|
refresh_dns
|
|
fi
|
|
|
|
rule_counter=0
|
|
fi
|
|
|
|
if [ -f $jobfile ]; then
|
|
|
|
mv $jobfile $jobfile.work
|
|
|
|
while read LINE; do
|
|
|
|
execute_task() {
|
|
case $2 in
|
|
fail) fail_wan $1;;
|
|
pass) recover_wan $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}')
|
|
execute_task $queued_task
|
|
done < $jobfile.work
|
|
|
|
rm $jobfile.work
|
|
fi
|
|
|
|
if [ "$health_monitor" = 'serial' ]; then
|
|
break
|
|
else
|
|
sleep 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
fail_start_check(){
|
|
local ipaddr
|
|
local gateway
|
|
local ifname
|
|
local group
|
|
|
|
local i=0
|
|
while [ $((i++)) -lt $wancount ]; do
|
|
group=$(query_config group $i)
|
|
ifname=$(query_config ifname $group)
|
|
ipaddr=$(query_config ipaddr $group)
|
|
gateway=$(query_config gateway $group)
|
|
|
|
if [ "$ifname" == "x" -o "$ipaddr" == "x" -o "$gateway" == "x" ]; then
|
|
failover add $group
|
|
fi
|
|
done
|
|
}
|
|
|
|
wancount=0
|
|
max_interval=$(((1<<31) - 1))
|
|
|
|
config_clear
|
|
config_load "multiwan"
|
|
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")
|
|
|
|
jobfile="/tmp/.mwan/jobqueue"
|
|
|
|
case $1 in
|
|
agent) silencer main_init;;
|
|
stop) silencer stop;;
|
|
restart) silencer stop restart;;
|
|
single) silencer stop single;;
|
|
esac
|