diff --git a/net/mwan3/Makefile b/net/mwan3/Makefile index adb54b24a..741c92ce7 100644 --- a/net/mwan3/Makefile +++ b/net/mwan3/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mwan3 -PKG_VERSION:=2.6.18 +PKG_VERSION:=2.7.0 PKG_RELEASE:=1 PKG_MAINTAINER:=Florian Eckert PKG_LICENSE:=GPLv2 diff --git a/net/mwan3/files/etc/config/mwan3 b/net/mwan3/files/etc/config/mwan3 index b7f6c2c11..966601eee 100644 --- a/net/mwan3/files/etc/config/mwan3 +++ b/net/mwan3/files/etc/config/mwan3 @@ -1,7 +1,8 @@ config globals 'globals' option mmx_mask '0x3F00' - option local_source 'lan' + option local_source 'none' + option rtmon_interval '5' config interface 'wan' option enabled '1' diff --git a/net/mwan3/files/etc/hotplug.d/iface/16-mwan3 b/net/mwan3/files/etc/hotplug.d/iface/16-mwan3 new file mode 100644 index 000000000..d2d148baf --- /dev/null +++ b/net/mwan3/files/etc/hotplug.d/iface/16-mwan3 @@ -0,0 +1,17 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/network.sh +. /lib/mwan3/mwan3.sh + +config_load mwan3 +config_get_bool enabled globals 'enabled' '0' +[ ${enabled} -gt 0 ] || exit 0 + +if [ "$ACTION" == "ifup" ]; then + mwan3_lock + mwan3_rtmon + mwan3_unlock +fi + +exit 0 diff --git a/net/mwan3/files/lib/mwan3/mwan3.sh b/net/mwan3/files/lib/mwan3/mwan3.sh index 2c5bbf3c1..f252cb26b 100644 --- a/net/mwan3/files/lib/mwan3/mwan3.sh +++ b/net/mwan3/files/lib/mwan3/mwan3.sh @@ -20,6 +20,63 @@ MM_BLACKHOLE="" MMX_UNREACHABLE="" MM_UNREACHABLE="" +mwan3_rtmon_ipv4() +{ + local tid=1 + local idx=0 + local ret=1 + main_tbsum=$($IP4 route list table main | grep -v ^default | md5sum | head -c32) + while uci get mwan3.@interface[$idx] >/dev/null 2>&1 ; do + idx=$((idx+1)) + tid=$idx + [ "$(uci get mwan3.@interface[$((idx-1))].family)" = "ipv4" ] && { + if $IP4 route list table $tid | grep -q ^default; then + tbsum=$($IP4 route list table $tid | grep -v ^default | md5sum | head -c32) + if [ "$tbsum" != "$main_tbsum" ]; then + $IP4 route list table $tid | grep -v ^default | while read line; do + $IP4 route del table $tid $line + done + $IP4 route list table main | grep -v ^default | while read line; do + $IP4 route add table $tid $line + done + fi + fi + } + if [ "$(uci get mwan3.@interface[$((idx-1))].enabled)" = "1" ]; then + ret=0 + fi + done + return $ret +} + +mwan3_rtmon_ipv6() +{ + local tid=1 + local idx=0 + local ret=1 + main_tbsum=$($IP6 route list table main | grep -v "^default\|^::/" | md5sum | head -c32) + while uci get mwan3.@interface[$idx] >/dev/null 2>&1 ; do + idx=$((idx+1)) + tid=$idx + [ "$(uci get mwan3.@interface[$((idx-1))].family)" = "ipv6" ] && { + if $IP6 route list table $tid | grep -q ^::/0; then + tbsum=$($IP6 route list table $tid | grep -v "^default\|^::/" | md5sum | head -c32) + if [ "$tbsum" != "$main_tbsum" ]; then + $IP6 route list table $tid | grep -v "^default\|^::/" | while read line; do + $IP6 route del table $tid $line + done + $IP6 route list table main | grep -v "^default\|^::/" | while read line; do + $IP6 route add table $tid $line + done + fi + fi + } + if [ "$(uci get mwan3.@interface[$((idx-1))].enabled)" = "1" ]; then + ret=0 + fi + done + return $ret +} # counts how many bits are set to 1 # n&(n-1) clears the lowest bit set to 1 @@ -188,10 +245,6 @@ mwan3_set_general_iptables() $IPT -A mwan3_connected -m set --match-set mwan3_connected dst -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK fi - if ! $IPT -S mwan3_ifaces_out &> /dev/null; then - $IPT -N mwan3_ifaces_out - fi - if ! $IPT -S mwan3_rules &> /dev/null; then $IPT -N mwan3_rules fi @@ -209,7 +262,6 @@ mwan3_set_general_iptables() $IPT -A mwan3_hook -j CONNMARK --restore-mark --nfmask $MMX_MASK --ctmask $MMX_MASK $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_ifaces_in $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_connected - $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_ifaces_out $IPT -A mwan3_hook -m mark --mark 0x0/$MMX_MASK -j mwan3_rules $IPT -A mwan3_hook -j CONNMARK --save-mark --nfmask $MMX_MASK --ctmask $MMX_MASK $IPT -A mwan3_hook -m mark ! --mark $MMX_DEFAULT/$MMX_MASK -j mwan3_connected @@ -241,30 +293,16 @@ mwan3_create_iface_iptables() $IPT4 -N mwan3_ifaces_in fi - if ! $IPT4 -S mwan3_ifaces_out &> /dev/null; then - $IPT4 -N mwan3_ifaces_out - fi - if ! $IPT4 -S mwan3_iface_in_$1 &> /dev/null; then $IPT4 -N mwan3_iface_in_$1 fi - if ! $IPT4 -S mwan3_iface_out_$1 &> /dev/null; then - $IPT4 -N mwan3_iface_out_$1 - fi - $IPT4 -F mwan3_iface_in_$1 $IPT4 -A mwan3_iface_in_$1 -i $2 -m set --match-set mwan3_connected src -m mark --mark 0x0/$MMX_MASK -m comment --comment "default" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK $IPT4 -A mwan3_iface_in_$1 -i $2 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK $IPT4 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null $IPT4 -A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 - - $IPT4 -F mwan3_iface_out_$1 - $IPT4 -A mwan3_iface_out_$1 -o $2 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK - - $IPT4 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null - $IPT4 -A mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 fi if [ "$family" == "ipv6" ]; then @@ -274,30 +312,16 @@ mwan3_create_iface_iptables() $IPT6 -N mwan3_ifaces_in fi - if ! $IPT6 -S mwan3_ifaces_out &> /dev/null; then - $IPT6 -N mwan3_ifaces_out - fi - if ! $IPT6 -S mwan3_iface_in_$1 &> /dev/null; then $IPT6 -N mwan3_iface_in_$1 fi - if ! $IPT6 -S mwan3_iface_out_$1 &> /dev/null; then - $IPT6 -N mwan3_iface_out_$1 - fi - $IPT6 -F mwan3_iface_in_$1 $IPT6 -A mwan3_iface_in_$1 -i $2 -m set --match-set mwan3_connected_v6 src -m mark --mark 0x0/$MMX_MASK -m comment --comment "default" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK $IPT6 -A mwan3_iface_in_$1 -i $2 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK $IPT6 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null $IPT6 -A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 - - $IPT6 -F mwan3_iface_out_$1 - $IPT6 -A mwan3_iface_out_$1 -o $2 -m mark --mark 0x0/$MMX_MASK -m comment --comment "$1" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK - - $IPT6 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null - $IPT6 -A mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 fi } @@ -310,10 +334,6 @@ mwan3_delete_iface_iptables() $IPT4 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null $IPT4 -F mwan3_iface_in_$1 &> /dev/null $IPT4 -X mwan3_iface_in_$1 &> /dev/null - - $IPT4 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null - $IPT4 -F mwan3_iface_out_$1 &> /dev/null - $IPT4 -X mwan3_iface_out_$1 &> /dev/null fi if [ "$family" == "ipv6" ]; then @@ -321,10 +341,6 @@ mwan3_delete_iface_iptables() $IPT6 -D mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_$1 &> /dev/null $IPT6 -F mwan3_iface_in_$1 &> /dev/null $IPT6 -X mwan3_iface_in_$1 &> /dev/null - - $IPT6 -D mwan3_ifaces_out -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_out_$1 &> /dev/null - $IPT6 -F mwan3_iface_out_$1 &> /dev/null - $IPT6 -X mwan3_iface_out_$1 &> /dev/null fi } @@ -352,6 +368,7 @@ mwan3_create_iface_route() $IP4 route flush table $id $IP4 route add table $id default $route_args dev $2 + mwan3_rtmon_ipv4 fi if [ "$family" == "ipv6" ]; then @@ -369,6 +386,7 @@ mwan3_create_iface_route() $IP6 route flush table $id $IP6 route add table $id default $route_args dev $2 + mwan3_rtmon_ipv6 fi } @@ -409,7 +427,7 @@ mwan3_create_iface_rules() $IP4 rule del pref $(($id+2000)) done - $IP4 rule add pref $(($id+1000)) iif $2 lookup main + $IP4 rule add pref $(($id+1000)) iif $2 lookup $id $IP4 rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup $id fi @@ -423,7 +441,7 @@ mwan3_create_iface_rules() $IP6 rule del pref $(($id+2000)) done - $IP6 rule add pref $(($id+1000)) iif $2 lookup main + $IP6 rule add pref $(($id+1000)) iif $2 lookup $id $IP6 rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup $id fi } @@ -475,6 +493,16 @@ mwan3_delete_iface_ipset_entries() done } +mwan3_rtmon() +{ + pid="$(pgrep -f mwan3rtmon)" + if [ "${pid}" != "" ]; then + kill -USR1 "${pid}" + else + [ -x /usr/sbin/mwan3rtmon ] && /usr/sbin/mwan3rtmon & + fi +} + mwan3_track() { local track_ip track_ips pid @@ -507,13 +535,14 @@ mwan3_track_signal() mwan3_set_policy() { - local iface_count id iface family metric probability weight + local iface_count id iface family metric probability weight device config_get iface $1 interface config_get metric $1 metric 1 config_get weight $1 weight 1 [ -n "$iface" ] || return 0 + network_get_device device $iface [ "$metric" -gt $DEFAULT_LOWEST_METRIC ] && $LOG warn "Member interface $iface has >$DEFAULT_LOWEST_METRIC metric. Not appending to policy" && return 0 mwan3_get_iface_id id $iface @@ -552,6 +581,11 @@ mwan3_set_policy() $IPT4 -I mwan3_policy_$policy -m mark --mark 0x0/$MMX_MASK $probability -m comment --comment "$iface $weight $total_weight_v4" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK fi + else + [ -n "$device" ] && { + $IPT4 -S mwan3_policy_$policy | grep -q '.*--comment ".* [0-9]* [0-9]*"' || \ + $IPT4 -I mwan3_policy_$policy -o $device -m mark --mark 0x0/$MMX_MASK -m comment --comment "out $iface $device" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK + } fi fi @@ -585,6 +619,11 @@ mwan3_set_policy() $IPT6 -I mwan3_policy_$policy -m mark --mark 0x0/$MMX_MASK $probability -m comment --comment "$iface $weight $total_weight_v6" -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK fi + else + [ -n "$device" ] && { + $IPT6 -S mwan3_policy_$policy | grep -q '.*--comment ".* [0-9]* [0-9]*"' || \ + $IPT6 -I mwan3_policy_$policy -o $device -m mark --mark 0x0/$MMX_MASK -m comment --comment "out $iface $device" -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK + } fi fi } @@ -649,7 +688,7 @@ mwan3_set_sticky_iptables() [ -n "$id" ] || return 0 for IPT in "$IPT4" "$IPT6"; do - if [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -a -n "$($IPT -S mwan3_iface_out_$1 2> /dev/null)" ]; then + if [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ]; then $IPT -I mwan3_rule_$rule -m mark --mark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK -m set ! --match-set mwan3_sticky_$rule src,src -j MARK --set-xmark 0x0/$MMX_MASK $IPT -I mwan3_rule_$rule -m mark --mark 0/$MMX_MASK -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK fi @@ -822,9 +861,9 @@ mwan3_report_iface_status() if [ -z "$id" -o -z "$device" ]; then result="unknown" - elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" -a -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" -a -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -a -n "$($IPT -S mwan3_iface_out_$1 2> /dev/null)" -a -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then + elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" -a -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" -a -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -a -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then result="$(mwan3_get_iface_hotplug_state $1)" - elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" -o -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" -o -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -o -n "$($IPT -S mwan3_iface_out_$1 2> /dev/null)" -o -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then + elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" -o -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" -o -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" -o -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then result="error" elif [ "$enabled" == "1" ]; then result="offline" @@ -858,16 +897,16 @@ mwan3_report_policies_v4() for policy in $($IPT4 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do echo "$policy:" | sed 's/mwan3_policy_//' - [ -n "$total_weight" ] || total_weight=$($IPT4 -S $policy | cut -s -d'"' -f2 | head -1 | awk '{print $3}') + [ -n "$total_weight" ] || total_weight=$($IPT4 -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}') if [ ! -z "${total_weight##*[!0-9]*}" ]; then - for iface in $($IPT4 -S $policy | cut -s -d'"' -f2 | awk '{print $1}'); do - weight=$($IPT4 -S $policy | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}') + for iface in $($IPT4 -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do + weight=$($IPT4 -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}') percent=$(($weight*100/$total_weight)) echo " $iface ($percent%)" done else - echo " $($IPT4 -S $policy | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')" + echo " $($IPT4 -S $policy | grep -v '.*--comment "out .*" .*$' | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')" fi unset total_weight @@ -883,16 +922,16 @@ mwan3_report_policies_v6() for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do echo "$policy:" | sed 's/mwan3_policy_//' - [ -n "$total_weight" ] || total_weight=$($IPT6 -S $policy | cut -s -d'"' -f2 | head -1 | awk '{print $3}') + [ -n "$total_weight" ] || total_weight=$($IPT6 -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}') if [ ! -z "${total_weight##*[!0-9]*}" ]; then - for iface in $($IPT6 -S $policy | cut -s -d'"' -f2 | awk '{print $1}'); do - weight=$($IPT6 -S $policy | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}') + for iface in $($IPT6 -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do + weight=$($IPT6 -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}') percent=$(($weight*100/$total_weight)) echo " $iface ($percent%)" done else - echo " $($IPT6 -S $policy | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')" + echo " $($IPT6 -S $policy | grep -v '.*--comment "out .*" .*$' | sed '/.*--comment \([^ ]*\) .*$/!d;s//\1/;q')" fi unset total_weight diff --git a/net/mwan3/files/usr/sbin/mwan3 b/net/mwan3/files/usr/sbin/mwan3 index a0c296f18..c10ffa8a5 100755 --- a/net/mwan3/files/usr/sbin/mwan3 +++ b/net/mwan3/files/usr/sbin/mwan3 @@ -155,6 +155,12 @@ stop() { local ipset route rule table IP IPT pid src_ip + for pid in $(pgrep -f "mwan3rtmon"); do + kill -TERM "$pid" > /dev/null 2>&1 + sleep 1 + kill -KILL "$pid" > /dev/null 2>&1 + done + for pid in $(pgrep -f "mwan3track"); do kill -TERM "$pid" > /dev/null 2>&1 sleep 1 diff --git a/net/mwan3/files/usr/sbin/mwan3rtmon b/net/mwan3/files/usr/sbin/mwan3rtmon new file mode 100755 index 000000000..667d0cc87 --- /dev/null +++ b/net/mwan3/files/usr/sbin/mwan3rtmon @@ -0,0 +1,32 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/mwan3/mwan3.sh + +LOG="logger -t $(basename "$0")[$$] -p" + +clean_up() { + $LOG notice "Stopping mwan3rtmon..." + exit 0 +} + +rtchange() { + $LOG info "Detect rtchange event." +} + +main() { + local rtmon_interval + trap clean_up TERM + trap rtchange USR1 + + config_load mwan3 + config_get rtmon_interval globals rtmon_interval '5' + + sleep 3 + while mwan3_rtmon_ipv4 || mwan3_rtmon_ipv6; do + [ "$rtmon_interval" = "0" ] && break + sleep $rtmon_interval + done +} + +main "$@"