#!/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_bool enabled config enabled '1' [ "$enabled" -gt 0 ] || exit 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