mwan3: improve startup performance; version 2.9.0

improve startup and runtime performance by

1) moving common startup procedures out of hotplug script when called
from mwan3 start
2) reducing calls to iptables to check status of rules
3) consolidating iptables updates and updating with iptables-restore
4) do not wait for kill if nothing was killed
5) running interface hotplug scripts in parallel
6) eliminate operations in hotplug script that check status on every
single interface unnecessarily
7) consolidate how mwan3track makes hotplug calls
8) do not restart mwan3track on connected events

This is a significant refactor, but should not result in any breaking
changes or require users to update their configurations.

version bump to 2.9.0

Signed-off-by: Aaron Goodman <aaronjg@stanford.edu>
This commit is contained in:
Aaron Goodman 2020-07-26 17:21:50 -04:00
parent 39f58789e6
commit c07f5230be
9 changed files with 730 additions and 580 deletions

View file

@ -8,8 +8,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=mwan3 PKG_NAME:=mwan3
PKG_VERSION:=2.8.12 PKG_VERSION:=2.9.0
PKG_RELEASE:=2 PKG_RELEASE:=1
PKG_MAINTAINER:=Florian Eckert <fe@dev.tdt.de> PKG_MAINTAINER:=Florian Eckert <fe@dev.tdt.de>
PKG_LICENSE:=GPL-2.0 PKG_LICENSE:=GPL-2.0

View file

@ -4,96 +4,101 @@
. /lib/functions/network.sh . /lib/functions/network.sh
. /lib/mwan3/mwan3.sh . /lib/mwan3/mwan3.sh
. /usr/share/libubox/jshn.sh . /usr/share/libubox/jshn.sh
. /lib/mwan3/common.sh
[ "$ACTION" == "ifup" -o "$ACTION" == "ifdown" ] || exit 1 SCRIPTNAME="mwan3-hotplug"
[ "$ACTION" = "ifup" ] || [ "$ACTION" = "ifdown" ] || [ "$ACTION" = "connected" ] || [ "$ACTION" = "disconnected" ] || exit 1
[ -n "$INTERFACE" ] || exit 2 [ -n "$INTERFACE" ] || exit 2
if ( [ "$ACTION" = "ifup" ] || [ "$ACTION" = "connected" ] ) && [ -z "$DEVICE" ]; then
if [ "$ACTION" == "ifup" ]; then LOG notice "$ACTION called on $INTERFACE with no device set"
[ -n "$DEVICE" ] || exit 3 exit 3
fi fi
mwan3_lock "$ACTION" "$INTERFACE" [ "$MWAN3_STARTUP" = 1 ] || mwan3_lock "$ACTION" "$INTERFACE"
config_load mwan3 config_load mwan3
config_get_bool enabled globals 'enabled' '0' config_get_bool enabled globals 'enabled' '0'
[ "${enabled}" -gt 0 ] || { [ "${enabled}" -gt 0 ] || {
mwan3_unlock "$ACTION" "$INTERFACE" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE"
LOG notice "mwan3 hotplug on $INTERFACE not called because globally disabled"
mwan3_flush_conntrack "$INTERFACE" "$ACTION" mwan3_flush_conntrack "$INTERFACE" "$ACTION"
exit 0 exit 0
} }
$IPT4 -S mwan3_hook &>/dev/null || {
mwan3_unlock "$ACTION" "$INTERFACE"
LOG warn "hotplug called on $INTERFACE before mwan3 has been set up"
exit 0
}
mwan3_init mwan3_init
mwan3_set_connected_iptables [ "$MWAN3_STARTUP" = 1 ] || {
mwan3_set_custom_ipset mwan3_set_connected_iptables
mwan3_set_custom_ipset
}
if [ "$MWAN3_STARTUP" != 1 ]; then
mwan3_set_user_iface_rules $INTERFACE $DEVICE
fi
config_get initial_state $INTERFACE initial_state "online" config_get initial_state $INTERFACE initial_state "online"
config_get_bool enabled $INTERFACE 'enabled' '0' config_get_bool enabled $INTERFACE 'enabled' '0'
[ "${enabled}" -eq 1 ] || { [ "${enabled}" -eq 1 ] || {
mwan3_unlock "$ACTION" "$INTERFACE" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE"
LOG notice "mwan3 hotplug on $INTERFACE not called because interface disabled"
exit 0 exit 0
} }
if [ "$ACTION" = "ifup" ]; then trackpid=$(pgrep -f "mwan3track $INTERFACE ")
config_get family $INTERFACE family ipv4
if [ "$family" = "ipv4" ]; then
ubus call network.interface.${INTERFACE}_4 status &>/dev/null
if [ "$?" -eq "0" ]; then
network_get_ipaddr src_ip ${INTERFACE}_4
else
network_get_ipaddr src_ip ${INTERFACE}
fi
[ -n "$src_ip" ] || src_ip="0.0.0.0"
elif [ "$family" = "ipv6" ]; then
ubus call network.interface.${INTERFACE}_6 status &>/dev/null
if [ "$?" -eq "0" ]; then
network_get_ipaddr6 src_ip ${INTERFACE}_6
else
network_get_ipaddr6 src_ip ${INTERFACE}
fi
[ -n "$src_ip" ] || src_ip="::"
fi
fi
if [ "$initial_state" = "offline" ]; then if [ "$initial_state" = "offline" ]; then
json_load "$(ubus call mwan3 status '{"section":"interfaces"}')" status=$(cat $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS 2>/dev/null || echo unknown)
json_select "interfaces"
json_select "${INTERFACE}"
json_get_var running running
json_get_var status status
else else
status=online status=online
running=1
fi fi
$LOG notice "Execute "$ACTION" event on interface $INTERFACE (${DEVICE:-unknown})" [ -z "$TRUE_INTERFACE" ] && mwan3_get_true_iface TRUE_INTERFACE $INTERFACE
binary_status=$status
[ "$binary_status" = "online" ] || binary_status=offline
LOG notice "Execute "$ACTION" event on interface $INTERFACE (${DEVICE:-unknown})"
case "$ACTION" in case "$ACTION" in
ifup) ifup|connected)
mwan3_set_general_rules
mwan3_set_general_iptables
mwan3_create_iface_iptables $INTERFACE $DEVICE mwan3_create_iface_iptables $INTERFACE $DEVICE
mwan3_create_iface_rules $INTERFACE $DEVICE mwan3_create_iface_rules $INTERFACE $DEVICE
mwan3_create_iface_route $INTERFACE $DEVICE mwan3_create_iface_route $INTERFACE $DEVICE
if [ "${running}" -eq 1 ] && [ "${status}" = "online" ]; then [ "$MWAN3_STARTUP" != 1 ] && mwan3_add_non_default_iface_route $INTERFACE $DEVICE
$LOG notice "Starting tracker on interface $INTERFACE (${DEVICE:-unknown})" mwan3_set_iface_hotplug_state $INTERFACE "$binary_status"
mwan3_set_iface_hotplug_state $INTERFACE "online"
mwan3_track $INTERFACE $DEVICE "online" "$src_ip" mwan3_get_src_ip src_ip "$TRUE_INTERFACE"
if [ -n "${trackpid}" ]; then
device_pid=$(pgrep -f "mwan3track $INTERFACE $DEVICE ")
if [ "$device_pid" = "$trackpid" ]; then
[ "$ACTION" = ifup ] && kill -USR2 "$trackpid"
else else
$LOG notice "Starting tracker on interface $INTERFACE (${DEVICE:-unknown})" mwan3_track $INTERFACE $DEVICE "$binary_status" "$src_ip"
mwan3_set_iface_hotplug_state $INTERFACE "offline" LOG notice "Restarted tracker [$!] on interface $INTERFACE (${DEVICE:-unknown})"
mwan3_track $INTERFACE $DEVICE "offline" "$src_ip"
fi fi
mwan3_set_policies_iptables else
mwan3_set_user_rules mwan3_track $INTERFACE $DEVICE "$binary_status" "$src_ip"
LOG notice "Started tracker [$!] on interface $INTERFACE (${DEVICE:-unknown})"
fi
[ "$MWAN3_STARTUP" != 1 ] && [ "$binary_status" == "online" ] && mwan3_set_policies_iptables
;; ;;
ifdown) ifdown|disconnected)
mwan3_set_iface_hotplug_state $INTERFACE "offline" mwan3_set_iface_hotplug_state $INTERFACE "offline"
mwan3_delete_iface_ipset_entries $INTERFACE mwan3_delete_iface_ipset_entries $INTERFACE
mwan3_track_signal $INTERFACE $DEVICE mwan3_delete_iface_rules $INTERFACE
mwan3_delete_iface_route $INTERFACE
mwan3_delete_iface_iptables $INTERFACE
if [ "$ACTION" = "ifdown" ]; then
[ -n "$trackpid" ] && kill -USR1 "$trackpid"
fi
mwan3_set_policies_iptables mwan3_set_policies_iptables
mwan3_set_user_rules
;; ;;
esac esac
[ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE"
mwan3_unlock "$ACTION" "$INTERFACE"
exit 0 exit 0

View file

@ -4,22 +4,22 @@
. /lib/functions.sh . /lib/functions.sh
. /lib/mwan3/mwan3.sh . /lib/mwan3/mwan3.sh
mwan3_lock "$ACTION" "user" [ "$MWAN3_STARTUP" = 1 ] || mwan3_lock "$ACTION" "$DEVICE-user"
config_load mwan3 config_load mwan3
config_get_bool enabled globals 'enabled' '0' config_get_bool enabled globals 'enabled' '0'
[ "${enabled}" -gt 0 ] || { [ "${enabled}" -gt 0 ] || {
mwan3_unlock "$ACTION" "user" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$DEVICE-user"
exit 0 exit 0
} }
config_get_bool enabled "$INTERFACE" enabled 0 config_get_bool enabled "$INTERFACE" enabled 0
[ "${enabled}" -eq 1 ] || { [ "${enabled}" -eq 1 ] || {
mwan3_unlock "$ACTION" "user" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$DEVICE-user"
exit 0 exit 0
} }
mwan3_unlock "$ACTION" "user" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$DEVICE-user"
env -i ACTION="$ACTION" INTERFACE="$INTERFACE" DEVICE="$DEVICE" \ env -i ACTION="$ACTION" INTERFACE="$INTERFACE" DEVICE="$DEVICE" \
/bin/sh /etc/mwan3.user /bin/sh /etc/mwan3.user

View file

@ -5,8 +5,9 @@ USE_PROCD=1
boot() { boot() {
. /lib/config/uci.sh . /lib/config/uci.sh
uci_toggle_state mwan3 globals enabled "1" # disabled until mwan3 start runs so hotplug scripts
mwan3_boot=1 # do not start prematurely
uci_toggle_state mwan3 globals enabled "0"
rc_procd start_service rc_procd start_service
} }
@ -20,7 +21,6 @@ reload_service() {
} }
start_service() { start_service() {
[ -n "${mwan3_boot}" ] && return 0
/usr/sbin/mwan3 start 1000>&- /usr/sbin/mwan3 start 1000>&-
} }

View file

@ -4,3 +4,14 @@ get_uptime() {
local uptime=$(cat /proc/uptime) local uptime=$(cat /proc/uptime)
echo "${uptime%%.*}" echo "${uptime%%.*}"
} }
SCRIPTNAME="$(basename "$0")"
LOG()
{
local facility=$1; shift
# in development, we want to show 'debug' level logs
# when this release is out of beta, the comment in the line below
# should be removed
[ "$facility" = "debug" ] && return
logger -t "$SCRIPTNAME[$$]" -p $facility "$*"
}

View file

@ -1,4 +1,5 @@
#!/bin/sh #!/bin/sh
. /usr/share/libubox/jshn.sh . /usr/share/libubox/jshn.sh
IP4="ip -4" IP4="ip -4"
@ -6,7 +7,8 @@ IP6="ip -6"
IPS="ipset" IPS="ipset"
IPT4="iptables -t mangle -w" IPT4="iptables -t mangle -w"
IPT6="ip6tables -t mangle -w" IPT6="ip6tables -t mangle -w"
LOG="logger -t mwan3[$$] -p" IPT4R="iptables-restore -T mangle -w -n"
IPT6R="ip6tables-restore -T mangle -w -n"
CONNTRACK_FILE="/proc/net/nf_conntrack" CONNTRACK_FILE="/proc/net/nf_conntrack"
IPv6_REGEX="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" IPv6_REGEX="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,7}:|" IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,7}:|"
@ -69,7 +71,7 @@ mwan3_update_dev_to_table()
mwan3_update_iface_to_table() mwan3_update_iface_to_table()
{ {
local _tid section family cfgtype curr_table _mwan3_iface_tbl local _tid
mwan3_iface_tbl=" " mwan3_iface_tbl=" "
update_table() update_table()
{ {
@ -163,14 +165,14 @@ mwan3_init()
else else
config_load mwan3 config_load mwan3
config_get MMX_MASK globals mmx_mask '0x3F00' config_get MMX_MASK globals mmx_mask '0x3F00'
echo "$MMX_MASK" > "${MWAN3_STATUS_DIR}/mmx_mask" echo "$MMX_MASK"| tr 'A-F' 'a-f' > "${MWAN3_STATUS_DIR}/mmx_mask"
$LOG notice "Using firewall mask ${MMX_MASK}" LOG debug "Using firewall mask ${MMX_MASK}"
bitcnt=$(mwan3_count_one_bits MMX_MASK) bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1)) mmdefault=$(((1<<bitcnt)-1))
MWAN3_INTERFACE_MAX=$(($mmdefault-3)) MWAN3_INTERFACE_MAX=$(($mmdefault-3))
uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX" uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX"
$LOG notice "Max interface count is ${MWAN3_INTERFACE_MAX}" LOG debug "Max interface count is ${MWAN3_INTERFACE_MAX}"
fi fi
# mark mask constants # mark mask constants
@ -187,14 +189,30 @@ mwan3_init()
mwan3_lock() { mwan3_lock() {
lock /var/run/mwan3.lock lock /var/run/mwan3.lock
# $LOG debug "$1 $2 (lock)" #LOG debug "$1 $2 (lock)"
} }
mwan3_unlock() { mwan3_unlock() {
# $LOG debug "$1 $2 (unlock)" #LOG debug "$1 $2 (unlock)"
lock -u /var/run/mwan3.lock lock -u /var/run/mwan3.lock
} }
mwan3_get_src_ip()
{
local family _src_ip true_iface
true_iface=$2
unset "$1"
config_get family "$true_iface" family ipv4
if [ "$family" = "ipv4" ]; then
network_get_ipaddr _src_ip "$true_iface"
[ -n "$_src_ip" ] || _src_ip="0.0.0.0"
elif [ "$family" = "ipv6" ]; then
network_get_ipaddr6 _src_ip "$true_iface"
[ -n "$_src_ip" ] || _src_ip="::"
fi
export "$1=$_src_ip"
}
mwan3_get_iface_id() mwan3_get_iface_id()
{ {
local _tmp local _tmp
@ -202,6 +220,7 @@ mwan3_get_iface_id()
_tmp="${mwan3_iface_tbl##* ${2}=}" _tmp="${mwan3_iface_tbl##* ${2}=}"
_tmp=${_tmp%% *} _tmp=${_tmp%% *}
export "$1=$_tmp" export "$1=$_tmp"
new_val=$_tmp
} }
mwan3_set_custom_ipset_v4() mwan3_set_custom_ipset_v4()
@ -209,8 +228,8 @@ mwan3_set_custom_ipset_v4()
local custom_network_v4 local custom_network_v4
for custom_network_v4 in $($IP4 route list table "$1" | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do for custom_network_v4 in $($IP4 route list table "$1" | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do
$LOG notice "Adding network $custom_network_v4 from table $1 to mwan3_custom_v4 ipset" LOG notice "Adding network $custom_network_v4 from table $1 to mwan3_custom_v4 ipset"
$IPS -! add mwan3_custom_v4_temp "$custom_network_v4" mwan3_push_update -! add mwan3_custom_v4 "$custom_network_v4"
done done
} }
@ -219,47 +238,48 @@ mwan3_set_custom_ipset_v6()
local custom_network_v6 local custom_network_v6
for custom_network_v6 in $($IP6 route list table "$1" | awk '{print $1}' | egrep "$IPv6_REGEX"); do for custom_network_v6 in $($IP6 route list table "$1" | awk '{print $1}' | egrep "$IPv6_REGEX"); do
$LOG notice "Adding network $custom_network_v6 from table $1 to mwan3_custom_v6 ipset" LOG notice "Adding network $custom_network_v6 from table $1 to mwan3_custom_v6 ipset"
$IPS -! add mwan3_custom_v6_temp "$custom_network_v6" mwan3_push_update -! add mwan3_custom_v6 "$custom_network_v6"
done done
} }
mwan3_set_custom_ipset() mwan3_set_custom_ipset()
{ {
$IPS -! create mwan3_custom_v4 hash:net local update=""
$IPS create mwan3_custom_v4_temp hash:net
mwan3_push_update -! create mwan3_custom_v4 hash:net
config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v4 config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v4
$IPS swap mwan3_custom_v4_temp mwan3_custom_v4
$IPS destroy mwan3_custom_v4_temp
mwan3_push_update -! create mwan3_custom_v6 hash:net family inet6
$IPS -! create mwan3_custom_v6 hash:net family inet6
$IPS create mwan3_custom_v6_temp hash:net family inet6
config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v6 config_list_foreach "globals" "rt_table_lookup" mwan3_set_custom_ipset_v6
$IPS swap mwan3_custom_v6_temp mwan3_custom_v6
$IPS destroy mwan3_custom_v6_temp
$IPS -! create mwan3_connected list:set mwan3_push_update -! create mwan3_connected list:set
$IPS -! add mwan3_connected mwan3_custom_v4 mwan3_push_update -! add mwan3_connected mwan3_custom_v4
$IPS -! add mwan3_connected mwan3_custom_v6 mwan3_push_update -! add mwan3_connected mwan3_custom_v6
error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_custom_ipset: $error"
} }
mwan3_set_connected_ipv4() mwan3_set_connected_ipv4()
{ {
local connected_network_v4 candidate_list cidr_list local connected_network_v4 candidate_list cidr_list
local ipv4regex='[0-9]{1,3}(\.[0-9]{1,3}){3}'
$IPS -! create mwan3_connected_v4 hash:net
$IPS create mwan3_connected_v4_temp hash:net
candidate_list="" candidate_list=""
cidr_list="" cidr_list=""
for connected_network_v4 in $($IP4 route | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do route_lists()
[ -z "${connected_network_v4##*/*}" ] && {
cidr_list="$cidr_list $connected_network_v4" || $IP4 route | awk '{print $1}'
candidate_list="$candidate_list $connected_network_v4" $IP4 route list table 0 | awk '{print $2}'
done }
for connected_network_v4 in $(route_lists | egrep "$ipv4regex"); do
for connected_network_v4 in $($IP4 route list table 0 | awk '{print $2}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do if [ -z "${connected_network_v4##*/*}" ]; then
[ -z "${connected_network_v4##*/*}" ] && cidr_list="$cidr_list $connected_network_v4"
cidr_list="$cidr_list $connected_network_v4" || else
candidate_list="$candidate_list $connected_network_v4" candidate_list="$candidate_list $connected_network_v4"
fi
done done
for connected_network_v4 in $cidr_list; do for connected_network_v4 in $cidr_list; do
@ -279,41 +299,35 @@ mwan3_set_connected_ipv4()
mwan3_set_connected_iptables() mwan3_set_connected_iptables()
{ {
local connected_network_v6 source_network_v6 local connected_network_v6 source_network_v6 error
local update=""
$IPS -! create mwan3_connected_v4 hash:net
$IPS create mwan3_connected_v4_temp hash:net
mwan3_set_connected_ipv4 mwan3_set_connected_ipv4
[ $NO_IPV6 -eq 0 ] && { [ $NO_IPV6 -eq 0 ] && {
$IPS -! create mwan3_connected_v6 hash:net family inet6 mwan3_push_update -! create mwan3_connected_v6 hash:net family inet6
$IPS create mwan3_connected_v6_temp hash:net family inet6 mwan3_push_update flush mwan3_connected_v6
for connected_network_v6 in $($IP6 route | awk '{print $1}' | egrep "$IPv6_REGEX"); do for connected_network_v6 in $($IP6 route | awk '{print $1}' | egrep "$IPv6_REGEX"); do
$IPS -! add mwan3_connected_v6_temp "$connected_network_v6" mwan3_push_update -! add mwan3_connected_v6 "$connected_network_v6"
done done
$IPS swap mwan3_connected_v6_temp mwan3_connected_v6
$IPS destroy mwan3_connected_v6_temp
$IPS -! create mwan3_source_v6 hash:net family inet6 mwan3_push_update -! create mwan3_source_v6 hash:net family inet6
$IPS create mwan3_source_v6_temp hash:net family inet6
for source_network_v6 in $($IP6 addr ls | sed -ne 's/ *inet6 \([^ \/]*\).* scope global.*/\1/p'); do for source_network_v6 in $($IP6 addr ls | sed -ne 's/ *inet6 \([^ \/]*\).* scope global.*/\1/p'); do
$IPS -! add mwan3_source_v6_temp "$source_network_v6" mwan3_push_update -! add mwan3_source_v6 "$source_network_v6"
done done
$IPS swap mwan3_source_v6_temp mwan3_source_v6
$IPS destroy mwan3_source_v6_temp
} }
$IPS -! create mwan3_connected list:set mwan3_push_update -! create mwan3_connected list:set
$IPS -! add mwan3_connected mwan3_connected_v4 mwan3_push_update flush mwan3_connected
[ $NO_IPV6 -eq 0 ] && $IPS -! add mwan3_connected mwan3_connected_v6 mwan3_push_update -! add mwan3_connected mwan3_connected_v4
[ $NO_IPV6 -eq 0 ] && mwan3_push_update -! add mwan3_connected mwan3_connected_v6
$IPS -! create mwan3_dynamic_v4 hash:net mwan3_push_update -! create mwan3_dynamic_v4 hash:net
$IPS -! add mwan3_connected mwan3_dynamic_v4 mwan3_push_update -! add mwan3_connected mwan3_dynamic_v4
[ $NO_IPV6 -eq 0 ] && $IPS -! create mwan3_dynamic_v6 hash:net family inet6 [ $NO_IPV6 -eq 0 ] && mwan3_push_update -! create mwan3_dynamic_v6 hash:net family inet6
[ $NO_IPV6 -eq 0 ] && $IPS -! add mwan3_connected mwan3_dynamic_v6 [ $NO_IPV6 -eq 0 ] && mwan3_push_update -! add mwan3_connected mwan3_dynamic_v6
error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_iptables: $error"
} }
mwan3_set_general_rules() mwan3_set_general_rules()
@ -336,89 +350,96 @@ mwan3_set_general_rules()
mwan3_set_general_iptables() mwan3_set_general_iptables()
{ {
local IPT local IPT current update error
for IPT in "$IPT4" "$IPT6"; do for IPT in "$IPT4" "$IPT6"; do
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
if ! $IPT -S mwan3_ifaces_in &> /dev/null; then current="$($IPT -S)"
$IPT -N mwan3_ifaces_in update="*mangle"
if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
mwan3_push_update -N mwan3_ifaces_in
fi fi
if ! $IPT -S mwan3_connected &> /dev/null; then if [ -n "${current##*-N mwan3_connected*}" ]; then
$IPT -N mwan3_connected mwan3_push_update -N mwan3_connected
$IPS -! create mwan3_connected list:set $IPS -! create mwan3_connected list:set
$IPT -A mwan3_connected \ mwan3_push_update -A mwan3_connected \
-m set --match-set mwan3_connected dst \ -m set --match-set mwan3_connected dst \
-j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
fi fi
if ! $IPT -S mwan3_rules &> /dev/null; then if [ -n "${current##*-N mwan3_rules*}" ]; then
$IPT -N mwan3_rules mwan3_push_update -N mwan3_rules
fi fi
if ! $IPT -S mwan3_hook &> /dev/null; then if [ -n "${current##*-N mwan3_hook*}" ]; then
$IPT -N mwan3_hook mwan3_push_update -N mwan3_hook
# do not mangle ipv6 ra service # do not mangle ipv6 ra service
if [ "$IPT" = "$IPT6" ]; then if [ "$IPT" = "$IPT6" ]; then
$IPT6 -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-p ipv6-icmp \ -p ipv6-icmp \
-m icmp6 --icmpv6-type 133 \ -m icmp6 --icmpv6-type 133 \
-j RETURN -j RETURN
$IPT6 -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-p ipv6-icmp \ -p ipv6-icmp \
-m icmp6 --icmpv6-type 134 \ -m icmp6 --icmpv6-type 134 \
-j RETURN -j RETURN
$IPT6 -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-p ipv6-icmp \ -p ipv6-icmp \
-m icmp6 --icmpv6-type 135 \ -m icmp6 --icmpv6-type 135 \
-j RETURN -j RETURN
$IPT6 -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-p ipv6-icmp \ -p ipv6-icmp \
-m icmp6 --icmpv6-type 136 \ -m icmp6 --icmpv6-type 136 \
-j RETURN -j RETURN
$IPT6 -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-p ipv6-icmp \ -p ipv6-icmp \
-m icmp6 --icmpv6-type 137 \ -m icmp6 --icmpv6-type 137 \
-j RETURN -j RETURN
# do not mangle outgoing echo request # do not mangle outgoing echo request
$IPT6 -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m set --match-set mwan3_source_v6 src \ -m set --match-set mwan3_source_v6 src \
-p ipv6-icmp \ -p ipv6-icmp \
-m icmp6 --icmpv6-type 128 \ -m icmp6 --icmpv6-type 128 \
-j RETURN -j RETURN
fi fi
$IPT -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" -j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
$IPT -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-j mwan3_ifaces_in -j mwan3_ifaces_in
$IPT -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-j mwan3_connected -j mwan3_connected
$IPT -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-j mwan3_rules -j mwan3_rules
$IPT -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-j CONNMARK --save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" -j CONNMARK --save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
$IPT -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m mark ! --mark $MMX_DEFAULT/$MMX_MASK \ -m mark ! --mark $MMX_DEFAULT/$MMX_MASK \
-j mwan3_connected -j mwan3_connected
fi fi
if ! $IPT -S PREROUTING | grep mwan3_hook &> /dev/null; then if [ -n "${current##*-A PREROUTING -j mwan3_hook*}" ]; then
$IPT -A PREROUTING -j mwan3_hook mwan3_push_update -A PREROUTING -j mwan3_hook
fi fi
if [ -n "${current##*-A OUTPUT -j mwan3_hook*}" ]; then
if ! $IPT -S OUTPUT | grep mwan3_hook &> /dev/null; then mwan3_push_update -A OUTPUT -j mwan3_hook
$IPT -A OUTPUT -j mwan3_hook fi
mwan3_push_update COMMIT
mwan3_push_update ""
if [ "$IPT" = "$IPT4" ]; then
error=$(echo "$update" | $IPT4R 2>&1) || LOG error "set_general_iptables: $error"
else
error=$(echo "$update" | $IPT6R 2>&1) || LOG error "set_general_iptables: $error"
fi fi
done done
} }
mwan3_create_iface_iptables() mwan3_create_iface_iptables()
{ {
local id family connected_name IPT local id family connected_name IPT IPTR current update error
config_get family "$1" family ipv4 config_get family "$1" family ipv4
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
@ -428,43 +449,53 @@ mwan3_create_iface_iptables()
if [ "$family" = "ipv4" ]; then if [ "$family" = "ipv4" ]; then
connected_name=mwan3_connected connected_name=mwan3_connected
IPT="$IPT4" IPT="$IPT4"
IPTR="$IPT4R"
$IPS -! create $connected_name list:set $IPS -! create $connected_name list:set
elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
connected_name=mwan3_connected_v6 connected_name=mwan3_connected_v6
IPT="$IPT6" IPT="$IPT6"
IPTR="$IPT6R"
$IPS -! create $connected_name hash:net family inet6 $IPS -! create $connected_name hash:net family inet6
else else
return return
fi fi
current="$($IPT -S)"
if ! $IPT -S mwan3_ifaces_in &> /dev/null; then update="*mangle"
$IPT -N mwan3_ifaces_in if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
mwan3_push_update -N mwan3_ifaces_in
fi fi
if ! $IPT -S "mwan3_iface_in_$1" &> /dev/null; then if [ -n "${current##*-N mwan3_iface_in_$1*}" ]; then
$IPT -N "mwan3_iface_in_$1" mwan3_push_update -N "mwan3_iface_in_$1"
else
mwan3_push_update -F "mwan3_iface_in_$1"
fi fi
$IPT -F "mwan3_iface_in_$1" mwan3_push_update -A "mwan3_iface_in_$1" \
$IPT -A "mwan3_iface_in_$1" \
-i "$2" \ -i "$2" \
-m set --match-set $connected_name src \ -m set --match-set $connected_name src \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "default" \ -m comment --comment "default" \
-j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
$IPT -A "mwan3_iface_in_$1" \ mwan3_push_update -A "mwan3_iface_in_$1" \
-i "$2" \ -i "$2" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "$1" \ -m comment --comment "$1" \
-j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
$IPT -D mwan3_ifaces_in \ if [ -n "${current##*-A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_${1}*}" ]; then
-m mark --mark 0x0/$MMX_MASK \ mwan3_push_update -A mwan3_ifaces_in \
-j "mwan3_iface_in_$1" &> /dev/null
$IPT -A mwan3_ifaces_in \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-j "mwan3_iface_in_$1" -j "mwan3_iface_in_$1"
LOG debug "create_iface_iptables: mwan3_iface_in_$1 not in iptables, adding"
else
LOG debug "create_iface_iptables: mwan3_iface_in_$1 already in iptables, skip"
fi
mwan3_push_update COMMIT
mwan3_push_update ""
error=$(echo "$update" | $IPTR 2>&1) || LOG error "create_iface_iptables: $error"
} }
@ -568,7 +599,6 @@ mwan3_add_all_nondefault_routes()
add_route() add_route()
{ {
let tid++ let tid++
config_get family "$section" family ipv4
[ -n "${active_tbls##* $tid *}" ] && return [ -n "${active_tbls##* $tid *}" ] && return
$IP route add table $tid $route_line || $IP route add table $tid $route_line ||
LOG warn "failed to add $route_line to table $tid" LOG warn "failed to add $route_line to table $tid"
@ -615,7 +645,7 @@ mwan3_delete_iface_route()
mwan3_create_iface_rules() mwan3_create_iface_rules()
{ {
local id family local id family IP
config_get family "$1" family ipv4 config_get family "$1" family ipv4
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
@ -623,32 +653,23 @@ mwan3_create_iface_rules()
[ -n "$id" ] || return 0 [ -n "$id" ] || return 0
if [ "$family" = "ipv4" ]; then if [ "$family" = "ipv4" ]; then
IP="$IP4"
while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
$IP4 rule del pref $(($id+1000)) IP="$IP6"
done else
return
while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
$IP4 rule del pref $(($id+2000))
done
$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 fi
if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then while [ -n "$($IP rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
$IP rule del pref $(($id+1000))
while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
$IP6 rule del pref $(($id+1000))
done done
while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do while [ -n "$($IP rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
$IP6 rule del pref $(($id+2000)) $IP rule del pref $(($id+2000))
done done
$IP6 rule add pref $(($id+1000)) iif "$2" lookup "$id" $IP 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" $IP rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup "$id"
fi
} }
mwan3_delete_iface_rules() mwan3_delete_iface_rules()
@ -661,26 +682,20 @@ mwan3_delete_iface_rules()
[ -n "$id" ] || return 0 [ -n "$id" ] || return 0
if [ "$family" = "ipv4" ]; then if [ "$family" = "ipv4" ]; then
IP="$IP4"
while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
$IP4 rule del pref $(($id+1000)) IP="$IP6"
done else
return
while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
$IP4 rule del pref $(($id+2000))
done
fi fi
if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then while [ -n "$($IP rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
$IP rule del pref $(($id+1000))
while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do
$IP6 rule del pref $(($id+1000))
done done
while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do while [ -n "$($IP rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
$IP6 rule del pref $(($id+2000)) $IP rule del pref $(($id+2000))
done done
fi
} }
mwan3_delete_iface_ipset_entries() mwan3_delete_iface_ipset_entries()
@ -692,7 +707,7 @@ mwan3_delete_iface_ipset_entries()
[ -n "$id" ] || return 0 [ -n "$id" ] || return 0
for setname in $(ipset -n list | grep ^mwan3_sticky_); do for setname in $(ipset -n list | grep ^mwan3_sticky_); do
for entry in $(ipset list "$setname" | grep "$(echo $(mwan3_id2mask id MMX_MASK) | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do for entry in $(ipset list "$setname" | grep "$(mwan3_id2mask id MMX_MASK | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do
$IPS del "$setname" $entry $IPS del "$setname" $entry
done done
done done
@ -712,7 +727,7 @@ mwan3_rtmon()
mwan3_track() mwan3_track()
{ {
local track_ip track_ips pid local track_ips pids
mwan3_list_track_ips() mwan3_list_track_ips()
{ {
@ -720,28 +735,21 @@ mwan3_track()
} }
config_list_foreach "$1" track_ip mwan3_list_track_ips config_list_foreach "$1" track_ip mwan3_list_track_ips
kill -TERM $(pgrep -f "mwan3track $1 $2") > /dev/null 2>&1 # don't match device in case it changed from last launch
if pids=$(pgrep -f "mwan3track $1 "); then
kill -TERM $pids > /dev/null 2>&1
sleep 1 sleep 1
kill -KILL $(pgrep -f "mwan3track $1 $2") > /dev/null 2>&1 kill -KILL $(pgrep -f "mwan3track $1 ") > /dev/null 2>&1
fi
if [ -n "$track_ips" ]; then if [ -n "$track_ips" ]; then
[ -x /usr/sbin/mwan3track ] && /usr/sbin/mwan3track "$1" "$2" "$3" "$4" $track_ips & [ -x /usr/sbin/mwan3track ] && MWAN3_STARTUP=0 /usr/sbin/mwan3track "$1" "$2" "$3" "$4" $track_ips &
fi fi
} }
mwan3_track_signal()
{
local pid
pid="$(pgrep -f "mwan3track $1 $2")"
[ "${pid}" != "" ] && {
kill -USR1 "${pid}"
}
}
mwan3_set_policy() mwan3_set_policy()
{ {
local iface_count id iface family metric probability weight device is_lowest is_offline IPT total_weight local id iface family metric probability weight device is_lowest is_offline IPT IPTR total_weight current update error
is_lowest=0 is_lowest=0
config_get iface "$1" interface config_get iface "$1" interface
@ -750,22 +758,26 @@ mwan3_set_policy()
[ -n "$iface" ] || return 0 [ -n "$iface" ] || return 0
network_get_device device "$iface" 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 [ "$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" mwan3_get_iface_id id "$iface"
[ -n "$id" ] || return 0
[ "$(mwan3_get_iface_hotplug_state "$iface")" = "online" ] [ "$(mwan3_get_iface_hotplug_state "$iface")" = "online" ]
is_offline=$? is_offline=$?
[ -n "$id" ] || return 0
config_get family "$iface" family ipv4 config_get family "$iface" family ipv4
if [ "$family" = "ipv4" ]; then if [ "$family" = "ipv4" ]; then
IPT="$IPT4" IPT="$IPT4"
IPTR="$IPT4R"
elif [ "$family" = "ipv6" ]; then elif [ "$family" = "ipv6" ]; then
IPT="$IPT6" IPT="$IPT6"
IPTR="$IPT6R"
fi fi
current="$($IPT -S)"
update="*mangle"
if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then
if [ "$metric" -lt "$lowest_metric_v4" ]; then if [ "$metric" -lt "$lowest_metric_v4" ]; then
@ -791,10 +803,10 @@ mwan3_set_policy()
fi fi
fi fi
if [ $is_lowest -eq 1 ]; then if [ $is_lowest -eq 1 ]; then
$IPT -F "mwan3_policy_$policy" mwan3_push_update -F "mwan3_policy_$policy"
$IPT -A "mwan3_policy_$policy" \ mwan3_push_update -A "mwan3_policy_$policy" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "$iface $weight $weight" \ -m comment --comment \"$iface $weight $weight\" \
-j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
elif [ $is_offline -eq 0 ]; then elif [ $is_offline -eq 0 ]; then
probability=$(($weight*1000/$total_weight)) probability=$(($weight*1000/$total_weight))
@ -808,63 +820,76 @@ mwan3_set_policy()
probability="1" probability="1"
fi fi
$IPT -I "mwan3_policy_$policy" \ mwan3_push_update -I "mwan3_policy_$policy" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m statistic \ -m statistic \
--mode random \ --mode random \
--probability "$probability" \ --probability "$probability" \
-m comment --comment "$iface $weight $total_weight" \ -m comment --comment \"$iface $weight $total_weight\" \
-j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
elif [ -n "$device" ]; then elif [ -n "$device" ]; then
$IPT -S "mwan3_policy_$policy" | grep -q '.*--comment ".* [0-9]* [0-9]*"' || \ echo "$current" | grep -q "^-A mwan3_policy_$policy.*--comment .* [0-9]* [0-9]*" ||
$IPT -I "mwan3_policy_$policy" \ mwan3_push_update -I "mwan3_policy_$policy" \
-o "$device" \ -o "$device" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "out $iface $device" \ -m comment --comment \"out $iface $device\" \
-j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
fi fi
mwan3_push_update COMMIT
mwan3_push_update ""
error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_policy ($1): $error"
} }
mwan3_create_policies_iptables() mwan3_create_policies_iptables()
{ {
local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT current update error
policy="$1" policy="$1"
config_get last_resort "$1" last_resort unreachable config_get last_resort "$1" last_resort unreachable
if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
$LOG warn "Policy $1 exceeds max of 15 chars. Not setting policy" && return 0 LOG warn "Policy $1 exceeds max of 15 chars. Not setting policy" && return 0
fi fi
for IPT in "$IPT4" "$IPT6"; do for IPT in "$IPT4" "$IPT6"; do
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
if ! $IPT -S "mwan3_policy_$1" &> /dev/null; then current="$($IPT -S)"
$IPT -N "mwan3_policy_$1" update="*mangle"
if [ -n "${current##*-N mwan3_policy_$1*}" ]; then
mwan3_push_update -N "mwan3_policy_$1"
fi fi
$IPT -F "mwan3_policy_$1" mwan3_push_update -F "mwan3_policy_$1"
case "$last_resort" in case "$last_resort" in
blackhole) blackhole)
$IPT -A "mwan3_policy_$1" \ mwan3_push_update -A "mwan3_policy_$1" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "blackhole" \ -m comment --comment "blackhole" \
-j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK
;; ;;
default) default)
$IPT -A "mwan3_policy_$1" \ mwan3_push_update -A "mwan3_policy_$1" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "default" \ -m comment --comment "default" \
-j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK
;; ;;
*) *)
$IPT -A "mwan3_policy_$1" \ mwan3_push_update -A "mwan3_policy_$1" \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
-m comment --comment "unreachable" \ -m comment --comment "unreachable" \
-j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK
;; ;;
esac esac
mwan3_push_update COMMIT
mwan3_push_update ""
if [ "$IPT" = "$IPT4" ]; then
error=$(echo "$update" | $IPT4R 2>&1) || LOG error "create_policies_iptables ($1): $error"
else
error=$(echo "$update" | $IPT6R 2>&1) || LOG error "create_policies_iptables ($1): $error"
fi
done done
lowest_metric_v4=$DEFAULT_LOWEST_METRIC lowest_metric_v4=$DEFAULT_LOWEST_METRIC
@ -884,27 +909,21 @@ mwan3_set_policies_iptables()
mwan3_set_sticky_iptables() mwan3_set_sticky_iptables()
{ {
local id iface local id iface
for iface in $(echo "$current" | grep "^-A $policy" | cut -s -d'"' -f2 | awk '{print $1}'); do
for iface in $($IPT4 -S "$policy" | cut -s -d'"' -f2 | awk '{print $1}'); do
if [ "$iface" = "$1" ]; then if [ "$iface" = "$1" ]; then
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
[ -n "$id" ] || return 0 [ -n "$id" ] || return 0
if [ -z "${current##*-N mwan3_iface_in_$1*}" ]; then
for IPT in "$IPT4" "$IPT6"; do mwan3_push_update -I "mwan3_rule_$rule" \
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continuea
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 mark --mark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK \
-m set ! --match-set "mwan3_sticky_$rule" src,src \ -m set ! --match-set "mwan3_sticky_$rule" src,src \
-j MARK --set-xmark 0x0/$MMX_MASK -j MARK --set-xmark 0x0/$MMX_MASK
$IPT -I "mwan3_rule_$rule" \ mwan3_push_update -I "mwan3_rule_$rule" \
-m mark --mark 0/$MMX_MASK \ -m mark --mark 0/$MMX_MASK \
-j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK
fi fi
done
fi fi
done done
} }
@ -912,53 +931,64 @@ mwan3_set_sticky_iptables()
mwan3_set_user_iptables_rule() mwan3_set_user_iptables_rule()
{ {
local ipset family proto policy src_ip src_port src_iface src_dev local ipset family proto policy src_ip src_port src_iface src_dev
local sticky dest_ip dest_port use_policy timeout rule policy IPT local sticky dest_ip dest_port use_policy timeout policy
local global_logging rule_logging loglevel local global_logging rule_logging loglevel rule_policy rule ipv
rule="$1" rule="$1"
ipv="$2"
rule_policy=0
config_get sticky "$1" sticky 0 config_get sticky "$1" sticky 0
config_get timeout "$1" timeout 600 config_get timeout "$1" timeout 600
config_get ipset "$1" ipset config_get ipset "$1" ipset
config_get proto "$1" proto all config_get proto "$1" proto all
config_get src_ip "$1" src_ip config_get src_ip "$1" src_ip
config_get src_iface "$1" src_iface config_get src_iface "$1" src_iface
network_get_device src_dev "$src_iface"
config_get src_port "$1" src_port config_get src_port "$1" src_port
config_get dest_ip "$1" dest_ip config_get dest_ip "$1" dest_ip
config_get dest_port "$1" dest_port config_get dest_port "$1" dest_port
config_get use_policy "$1" use_policy config_get use_policy "$1" use_policy
config_get family "$1" family any config_get family "$1" family any
config_get rule_logging "$1" logging 0
config_get global_logging globals logging 0
config_get loglevel globals loglevel notice
if [ -n "$src_iface" ]; then
network_get_device src_dev "$src_iface"
if [ -z "$src_dev" ]; then
LOG notice "could not find device corresponding to src_iface $src_iface for rule $1"
return
fi
fi
[ -z "$dest_ip" ] && unset dest_ip [ -z "$dest_ip" ] && unset dest_ip
[ -z "$src_ip" ] && unset src_ip [ -z "$src_ip" ] && unset src_ip
[ -z "$ipset" ] && unset ipset [ -z "$ipset" ] && unset ipset
[ -z "$src_port" ] && unset src_port [ -z "$src_port" ] && unset src_port
[ -z "$dest_port" ] && unset dest_port [ -z "$dest_port" ] && unset dest_port
[ "$proto" != 'tcp' ] && [ "$proto" != 'udp' ] && { if [ "$proto" != 'tcp' ] && [ "$proto" != 'udp' ]; then
[ -n "$src_port" ] && { [ -n "$src_port" ] && {
$LOG warn "src_port set to '$src_port' but proto set to '$proto' not tcp or udp. src_port will be ignored" LOG warn "src_port set to '$src_port' but proto set to '$proto' not tcp or udp. src_port will be ignored"
} }
[ -n "$dest_port" ] && { [ -n "$dest_port" ] && {
$LOG warn "dest_port set to '$dest_port' but proto set to '$proto' not tcp or udp. dest_port will be ignored" LOG warn "dest_port set to '$dest_port' but proto set to '$proto' not tcp or udp. dest_port will be ignored"
} }
unset src_port unset src_port
unset dest_port unset dest_port
} fi
config_get rule_logging "$1" logging 0
config_get global_logging globals logging 0
config_get loglevel globals loglevel notice
if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then if [ "$1" != "$(echo "$1" | cut -c1-15)" ]; then
$LOG warn "Rule $1 exceeds max of 15 chars. Not setting rule" && return 0 LOG warn "Rule $1 exceeds max of 15 chars. Not setting rule" && return 0
fi fi
if [ -n "$ipset" ]; then if [ -n "$ipset" ]; then
ipset="-m set --match-set $ipset dst" ipset="-m set --match-set $ipset dst"
fi fi
if [ -n "$use_policy" ]; then if [ -z "$use_policy" ]; then
return
fi
if [ "$use_policy" = "default" ]; then if [ "$use_policy" = "default" ]; then
policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK" policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK"
elif [ "$use_policy" = "unreachable" ]; then elif [ "$use_policy" = "unreachable" ]; then
@ -966,67 +996,53 @@ mwan3_set_user_iptables_rule()
elif [ "$use_policy" = "blackhole" ]; then elif [ "$use_policy" = "blackhole" ]; then
policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK" policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK"
else else
if [ "$sticky" -eq 1 ]; then rule_policy=1
policy="mwan3_policy_$use_policy" policy="mwan3_policy_$use_policy"
if [ "$sticky" -eq 1 ]; then
for IPT in "$IPT4" "$IPT6"; do
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
if ! $IPT -S "$policy" &> /dev/null; then
$IPT -N "$policy"
fi
if ! $IPT -S "mwan3_rule_$1" &> /dev/null; then
$IPT -N "mwan3_rule_$1"
fi
$IPT -F "mwan3_rule_$1"
done
$IPS -! create "mwan3_sticky_v4_$rule" \ $IPS -! create "mwan3_sticky_v4_$rule" \
hash:ip,mark markmask "$MMX_MASK" \ hash:ip,mark markmask "$MMX_MASK" \
timeout "$timeout" timeout "$timeout"
[ $NO_IPV6 -eq 0 ] &&
$IPS -! create "mwan3_sticky_v6_$rule" \ $IPS -! create "mwan3_sticky_v6_$rule" \
hash:ip,mark markmask "$MMX_MASK" \ hash:ip,mark markmask "$MMX_MASK" \
timeout "$timeout" family inet6 timeout "$timeout" family inet6
$IPS -! create "mwan3_sticky_$rule" list:set $IPS -! create "mwan3_sticky_$rule" list:set
$IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v4_$rule" $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v4_$rule"
[ $NO_IPV6 -eq 0 ] &&
$IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v6_$rule" $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v6_$rule"
fi
fi
config_foreach mwan3_set_sticky_iptables interface [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
[ "$family" = "ipv4" ] && [ "$ipv" = "ipv6" ] && return
[ "$family" = "ipv6" ] && [ "$ipv" = "ipv4" ] && return
for IPT in "$IPT4" "$IPT6"; do if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy*}" ]; then
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue mwan3_push_update -N "$policy"
$IPT -A "mwan3_rule_$1" \ fi
if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then
if [ -n "${current##*-N mwan3_rule_$1*}" ]; then
mwan3_push_update -N "mwan3_rule_$1"
fi
mwan3_push_update -F "mwan3_rule_$1"
config_foreach mwan3_set_sticky_iptables interface $ipv
mwan3_push_update -A "mwan3_rule_$1" \
-m mark --mark 0/$MMX_MASK \ -m mark --mark 0/$MMX_MASK \
-j "$policy" -j "$policy"
$IPT -A "mwan3_rule_$1" \ mwan3_push_update -A "mwan3_rule_$1" \
-m mark ! --mark 0xfc00/0xfc00 \ -m mark ! --mark 0xfc00/0xfc00 \
-j SET --del-set "mwan3_sticky_$rule" src,src -j SET --del-set "mwan3_sticky_$rule" src,src
$IPT -A "mwan3_rule_$1" \ mwan3_push_update -A "mwan3_rule_$1" \
-m mark ! --mark 0xfc00/0xfc00 \ -m mark ! --mark 0xfc00/0xfc00 \
-j SET --add-set "mwan3_sticky_$rule" src,src -j SET --add-set "mwan3_sticky_$rule" src,src
done
policy="mwan3_rule_$1" policy="mwan3_rule_$1"
else
policy="mwan3_policy_$use_policy"
for IPT in "$IPT4" "$IPT6"; do
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
if ! $IPT -S "$policy" &> /dev/null; then
$IPT -N "$policy"
fi fi
done if [ "$global_logging" = "1" ] && [ "$rule_logging" = "1" ]; then
mwan3_push_update -A mwan3_rules \
fi
fi
for IPT in "$IPT4" "$IPT6"; do
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue
[ "$family" = "ipv4" ] && [ "$IPT" = "$IPT6" ] && continue
[ "$family" = "ipv6" ] && [ "$IPT" = "$IPT4" ] && continue
[ "$global_logging" = "1" ] && [ "$rule_logging" = "1" ] && {
$IPT -A mwan3_rules \
-p "$proto" \ -p "$proto" \
${src_ip:+-s} $src_ip \ ${src_ip:+-s} $src_ip \
${src_dev:+-i} $src_dev \ ${src_dev:+-i} $src_dev \
@ -1037,9 +1053,9 @@ mwan3_set_user_iptables_rule()
-m mark --mark 0/$MMX_MASK \ -m mark --mark 0/$MMX_MASK \
-m comment --comment "$1" \ -m comment --comment "$1" \
-j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)" -j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)"
} fi
$IPT -A mwan3_rules \ mwan3_push_update -A mwan3_rules \
-p "$proto" \ -p "$proto" \
${src_ip:+-s} $src_ip \ ${src_ip:+-s} $src_ip \
${src_dev:+-i} $src_dev \ ${src_dev:+-i} $src_dev \
@ -1049,24 +1065,75 @@ mwan3_set_user_iptables_rule()
${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \ ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \
-m mark --mark 0/$MMX_MASK \ -m mark --mark 0/$MMX_MASK \
-j $policy -j $policy
done
}
mwan3_set_user_iface_rules()
{
local current iface update family error device is_src_iface
iface=$1
device=$2
if [ -z "$device" ]; then
LOG notice "set_user_iface_rules: could not find device corresponding to iface $iface"
return
fi fi
config_get family "$iface" family ipv4
if [ "$family" = "ipv4" ]; then
IPT="$IPT4"
IPTR="$IPT4R"
elif [ "$family" = "ipv6" ]; then
IPT="$IPT6"
IPTR="$IPT6R"
fi
$IPT -S | grep -q "^-A mwan3_rules.*-i $device" && return
is_src_iface=0
iface_rule()
{
local src_iface
config_get src_iface "$1" src_iface
[ "$src_iface" = "$iface" ] && is_src_iface=1
}
config_foreach iface_rule rule
[ $is_src_iface -eq 1 ] && mwan3_set_user_rules
} }
mwan3_set_user_rules() mwan3_set_user_rules()
{ {
local IPT local IPT IPTR ipv
local current update error
for IPT in "$IPT4" "$IPT6"; do for ipv in ipv4 ipv6; do
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue if [ "$ipv" = "ipv4" ]; then
if ! $IPT -S mwan3_rules &> /dev/null; then IPT="$IPT4"
$IPT -N mwan3_rules IPTR="$IPT4R"
elif [ "$ipv" = "ipv6" ]; then
IPT="$IPT6"
IPTR="$IPT6R"
fi
[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
update="*mangle"
current="$($IPT -S)"
if [ -n "${current##*-N mwan3_rules*}" ]; then
mwan3_push_update -N "mwan3_rules"
fi fi
$IPT -F mwan3_rules mwan3_push_update -F mwan3_rules
config_foreach mwan3_set_user_iptables_rule rule "$ipv"
mwan3_push_update COMMIT
mwan3_push_update ""
error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_user_rules: $error"
done done
config_foreach mwan3_set_user_iptables_rule rule
} }
mwan3_set_iface_hotplug_state() { mwan3_set_iface_hotplug_state() {
@ -1228,7 +1295,7 @@ mwan3_flush_conntrack()
if [ "$action" = "$flush_conntrack" ]; then if [ "$action" = "$flush_conntrack" ]; then
echo f > ${CONNTRACK_FILE} echo f > ${CONNTRACK_FILE}
$LOG info "Connection tracking flushed for interface '$interface' on action '$action'" LOG info "Connection tracking flushed for interface '$interface' on action '$action'"
fi fi
} }
@ -1240,9 +1307,5 @@ mwan3_flush_conntrack()
mwan3_track_clean() mwan3_track_clean()
{ {
rm -rf "$MWAN3_STATUS_DIR/${1}" &> /dev/null rm -rf "$MWAN3_STATUS_DIR/${1}" &> /dev/null
[ -d "$MWAN3_STATUS_DIR" ] && { rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR"
if [ -z "$(ls -A "$MWAN3_STATUS_DIR")" ]; then
rm -rf "$MWAN3_STATUS_DIR"
fi
}
} }

View file

@ -4,6 +4,7 @@
. /usr/share/libubox/jshn.sh . /usr/share/libubox/jshn.sh
. /lib/functions/network.sh . /lib/functions/network.sh
. /lib/mwan3/mwan3.sh . /lib/mwan3/mwan3.sh
. /lib/mwan3/common.sh
help() help()
{ {
@ -37,52 +38,64 @@ ifdown()
ACTION=ifdown INTERFACE=$1 /sbin/hotplug-call iface ACTION=ifdown INTERFACE=$1 /sbin/hotplug-call iface
kill $(pgrep -f "mwan3track $1 $2") &> /dev/null kill $(pgrep -f "mwan3track $1 ") &> /dev/null
mwan3_track_clean $1 mwan3_track_clean $1
} }
ifup() ifup()
{ {
local device enabled up l3_device status local device enabled up l3_device status interface true_iface
mwan3_lock "command" "mwan3" if [ -z "$1" ]; then
echo "Expecting interface. Usage: mwan3 ifup <interface>"
exit 0
fi
if [ -n "$2" ]; then
echo "Too many arguments. Usage: mwan3 ifup <interface>"
exit 0
fi
interface=$1
if [ "${MWAN3_STARTUP}" != 1 ]; then
# It is not necessary to obtain a lock here, because it is obtained in the hotplug
# script, but we still want to do the check to print a useful error message
config_load mwan3 config_load mwan3
config_get_bool enabled globals 'enabled' 0 config_get_bool enabled globals 'enabled' 0
[ ${enabled} -gt 0 ] || { [ ${enabled} -gt 0 ] || {
echo "The service mwan3 is global disabled." echo "The service mwan3 is global disabled."
echo "Please execute \"/etc/init.d/mwan3 start\" first." echo "Please execute \"/etc/init.d/mwan3 start\" first."
mwan3_unlock "command" "mwan3"
exit 1 exit 1
} }
else
if [ -z "$1" ]; then enabled=1
echo "Expecting interface. Usage: mwan3 ifup <interface>"
mwan3_unlock "command" "mwan3"
exit 0
fi fi
mwan3_get_true_iface true_iface $interface
status=$(ubus -S call network.interface.$true_iface status)
if [ -n "$2" ]; then
echo "Too many arguments. Usage: mwan3 ifup <interface>"
mwan3_unlock "command" "mwan3"
exit 0
fi
config_get enabled "$1" enabled 0
mwan3_unlock "command" "mwan3"
status=$(ubus -S call network.interface.$1 status)
[ -n "$status" ] && { [ -n "$status" ] && {
json_load "$status" json_load "$status"
json_get_vars up l3_device json_get_vars up l3_device
} }
hotplug_startup()
{
MWAN3_STARTUP=$MWAN3_STARTUP ACTION=ifup INTERFACE=$interface DEVICE=$l3_device TRUE_INTERFACE=$true_iface sh /etc/hotplug.d/iface/15-mwan3
MWAN3_STARTUP=$MWAN3_STARTUP ACTION=ifup INTERFACE=$interface DEVICE=$l3_device TRUE_INTERFACE=$true_iface sh /etc/hotplug.d/iface/16-mwan3-user
}
if [ "$up" = "1" ] \ if [ "$up" != "1" ] || [ -z "$l3_device" ] || [ "$enabled" != "1" ]; then
&& [ -n "$l3_device" ] \ return
&& [ "$enabled" = "1" ]; then
ACTION=ifup INTERFACE=$1 DEVICE=$l3_device /sbin/hotplug-call iface
fi fi
if [ "${MWAN3_STARTUP}" = 1 ]; then
hotplug_startup &
hotplug_pids="$hotplug_pids $!"
else
hotplug_startup
fi
} }
interfaces() interfaces()
@ -137,24 +150,37 @@ status()
start() start()
{ {
local enabled local enabled hotplug_pids MWAN3_STARTUP
MWAN3_STARTUP=1
mwan3_lock "command" "mwan3" mwan3_lock "command" "mwan3"
uci_toggle_state mwan3 globals enabled "1" uci_toggle_state mwan3 globals enabled "1"
mwan3_unlock "command" "mwan3"
config_load mwan3 config_load mwan3
mwan3_update_iface_to_table
mwan3_set_connected_iptables
mwan3_set_custom_ipset
mwan3_set_general_rules
mwan3_set_general_iptables
config_foreach ifup interface config_foreach ifup interface
wait $hotplug_pids
mwan3_add_all_nondefault_routes
mwan3_set_policies_iptables
mwan3_set_user_rules
mwan3_unlock "command" "mwan3"
mwan3_rtmon mwan3_rtmon
unset MWAN3_STARTUP
} }
stop() stop()
{ {
local ipset route rule table IP IPT pid local ipset rule IP IPTR IPT kill_pid family table tid
mwan3_lock "command" "mwan3" mwan3_lock "command" "mwan3"
uci_toggle_state mwan3 globals enabled "0" uci_toggle_state mwan3 globals enabled "0"
{
kill -TERM $(pgrep -f "mwan3rtmon") > /dev/null 2>&1 kill -TERM $(pgrep -f "mwan3rtmon") > /dev/null 2>&1
kill -TERM $(pgrep -f "mwan3track") > /dev/null 2>&1 kill -TERM $(pgrep -f "mwan3track") > /dev/null 2>&1
@ -162,33 +188,40 @@ stop()
kill -KILL $(pgrep -f "mwan3rtmon") > /dev/null 2>&1 kill -KILL $(pgrep -f "mwan3rtmon") > /dev/null 2>&1
kill -KILL $(pgrep -f "mwan3track") > /dev/null 2>&1 kill -KILL $(pgrep -f "mwan3track") > /dev/null 2>&1
} &
kill_pid=$!
config_load mwan3 config_load mwan3
config_foreach mwan3_track_clean interface config_foreach mwan3_track_clean interface
for IP in "$IP4" "$IP6"; do for family in ipv4 ipv6; do
[ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue if [ "$family" = "ipv4" ]; then
for route in $(seq 1 $MWAN3_INTERFACE_MAX); do IPT="$IPT4"
$IP route flush table $route &> /dev/null IPTR="$IPT4R"
IP="$IP4"
elif [ "$family" = "ipv6" ]; then
[ $NO_IPV6 -ne 0 ] && continue
IPT="$IPT6"
IPTR="$IPT6R"
IP="$IP6"
fi
for tid in $(ip route list table all | sed -ne 's/.*table \([0-9]\+\).*/\1/p'|sort -u); do
[ $tid -gt $MWAN3_INTERFACE_MAX ] && continue
$IP route flush table $tid &> /dev/null
done done
for rule in $($IP rule list | egrep '^[1-2][0-9]{3}\:' | cut -d ':' -f 1); do for rule in $($IP rule list | egrep '^[1-2][0-9]{3}\:' | cut -d ':' -f 1); do
$IP rule del pref $rule &> /dev/null $IP rule del pref $rule &> /dev/null
done done
done table="$($IPT -S)"
{
for IPT in "$IPT4" "$IPT6"; do echo "*mangle";
[ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue [ -z "${table##*PREROUTING -j mwan3_hook*}" ] && echo "-D PREROUTING -j mwan3_hook"
$IPT -D PREROUTING -j mwan3_hook &> /dev/null [ -z "${table##*OUTPUT -j mwan3_hook*}" ] && echo "-D OUTPUT -j mwan3_hook"
$IPT -D OUTPUT -j mwan3_hook &> /dev/null echo "$table" | awk '{print "-F "$2}' | grep mwan3 | sort -u
echo "$table" | awk '{print "-X "$2}' | grep mwan3 | sort -u
for table in $($IPT -S | awk '{print $2}' | grep mwan3 | sort -u); do echo "COMMIT"
$IPT -F $table &> /dev/null } | $IPTR
done
for table in $($IPT -S | awk '{print $2}' | grep mwan3 | sort -u); do
$IPT -X $table &> /dev/null
done
done done
for ipset in $($IPS -n list | grep mwan3_); do for ipset in $($IPS -n list | grep mwan3_); do
@ -199,9 +232,19 @@ stop()
$IPS -q destroy $ipset $IPS -q destroy $ipset
done done
if ! pgrep -f "mwan3track" >/dev/null && ! pgrep -f "mwan3rtmon" >/dev/null; then
# mwan3track has already exited, no need to send
# TERM signal
kill $kill_pid 2>/dev/null
else
# mwan3track has not exited, wait for the killer
# to do its work
wait $kill_pid
fi
rm -rf $MWAN3_STATUS_DIR $MWAN3TRACK_STATUS_DIR
mwan3_unlock "command" "mwan3" mwan3_unlock "command" "mwan3"
rm -rf $MWAN3_STATUS_DIR $MWAN3TRACK_STATUS_DIR
} }
restart() { restart() {

View file

@ -1,8 +1,9 @@
#!/bin/sh #!/bin/sh
. /lib/functions.sh . /lib/functions.sh
. /lib/functions/network.sh . /lib/functions/network.sh
. /lib/mwan3/mwan3.sh . /lib/mwan3/mwan3.sh
. /lib/mwan3/common.sh
mwan3_rtmon_route_handle() mwan3_rtmon_route_handle()
{ {

View file

@ -3,60 +3,107 @@
. /lib/functions.sh . /lib/functions.sh
. /lib/mwan3/common.sh . /lib/mwan3/common.sh
LOG="logger -t $(basename "$0")[$$] -p"
INTERFACE="" INTERFACE=""
DEVICE="" DEVICE=""
PING="/bin/ping" PING="/bin/ping"
IFDOWN_EVENT=0 IFDOWN_EVENT=0
IFUP_EVENT=0
clean_up() { clean_up() {
$LOG notice "Stopping mwan3track for interface \"${INTERFACE}\"" LOG notice "Stopping mwan3track for interface \"${INTERFACE}\""
exit 0 exit 0
} }
if_down() { if_down() {
$LOG info "Detect ifdown event on interface ${INTERFACE} (${DEVICE})" LOG info "Detect ifdown event on interface ${INTERFACE} (${DEVICE})"
IFDOWN_EVENT=1 IFDOWN_EVENT=1
} }
if_up() {
LOG info "Detect ifup event on interface ${INTERFACE} (${DEVICE})"
IFUP_EVENT=1
}
validate_track_method() { validate_track_method() {
case "$1" in case "$1" in
ping) ping)
command -v ping 1>/dev/null 2>&1 || { [ -x "$PING" ] || {
$LOG warn "Missing ping. Please install iputils-ping package or enable ping util and recompile busybox." LOG warn "Missing ping. Please enable ping util and recompile busybox."
return 1 return 1
} }
;; ;;
arping) arping)
command -v arping 1>/dev/null 2>&1 || { command -v arping 1>/dev/null 2>&1 || {
$LOG warn "Missing arping. Please install iputils-arping package." LOG warn "Missing arping. Please install iputils-arping package."
return 1 return 1
} }
;; ;;
httping) httping)
command -v httping 1>/dev/null 2>&1 || { command -v httping 1>/dev/null 2>&1 || {
$LOG warn "Missing httping. Please install httping package." LOG warn "Missing httping. Please install httping package."
return 1 return 1
} }
[ -n "$2" -a "$2" != "0.0.0.0" -a "$2" != "::" ] || { [ -n "$2" -a "$2" != "0.0.0.0" -a "$2" != "::" ] || {
$LOG warn "Cannot determine source IP for the interface which is required by httping." LOG warn "Cannot determine source IP for the interface which is required by httping."
return 1 return 1
} }
;; ;;
nping-*) nping-*)
command -v nping 1>/dev/null 2>&1 || { command -v nping 1>/dev/null 2>&1 || {
$LOG warn "Missing nping. Please install nping package." LOG warn "Missing nping. Please install nping package."
return 1 return 1
} }
;; ;;
*) *)
$LOG warn "Unsupported tracking method: $track_method" LOG warn "Unsupported tracking method: $track_method"
return 2 return 2
;; ;;
esac esac
} }
disconnected() {
echo "offline" > /var/run/mwan3track/$INTERFACE/STATUS
echo "$(get_uptime)" > /var/run/mwan3track/$INTERFACE/OFFLINE
echo "0" > /var/run/mwan3track/$INTERFACE/ONLINE
score=0
[ "$1" == 1 ] && return
LOG notice "Interface $INTERFACE ($DEVICE) is offline"
env -i ACTION="disconnected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
}
connected() {
echo "online" > /var/run/mwan3track/$INTERFACE/STATUS
echo "0" > /var/run/mwan3track/$INTERFACE/OFFLINE
echo "$(get_uptime)" > /var/run/mwan3track/$INTERFACE/ONLINE
host_up_count=0
lost=0
turn=0
loss=0
[ "$1" == 1 ] && return
LOG notice "Interface $INTERFACE ($DEVICE) is online"
env -i ACTION="connected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
}
firstconnect() {
if [ "$STATUS" = "offline" ]; then
disconnected 1
else
connected 1
fi
}
update_status() {
local status track_ip
track_ip=$1
status=$2
echo "$1" > /var/run/mwan3track/$INTERFACE/TRACK_${track_ip}
[ -z "$3" ] && return
echo "$3" > /var/run/mwan3track/$INTERFACE/LATENCY_${track_ip}
echo "$4" > /var/run/mwan3track/$INTERFACE/LOSS_${track_ip}
}
main() { main() {
local reliability count timeout interval failure_interval local reliability count timeout interval failure_interval
local recovery_interval down up size local recovery_interval down up size
@ -70,64 +117,49 @@ main() {
DEVICE=$2 DEVICE=$2
STATUS=$3 STATUS=$3
SRC_IP=$4 SRC_IP=$4
mkdir -p /var/run/mwan3track/$1 mkdir -p /var/run/mwan3track/$INTERFACE
trap clean_up TERM trap clean_up TERM
trap if_down USR1 trap if_down USR1
trap if_up USR2
config_load mwan3 config_load mwan3
config_get track_method $1 track_method ping config_get track_method $INTERFACE track_method ping
config_get_bool httping_ssl $1 httping_ssl 0 config_get_bool httping_ssl $INTERFACE httping_ssl 0
validate_track_method $track_method $SRC_IP || { validate_track_method $track_method $SRC_IP || {
track_method=ping track_method=ping
if validate_track_method $track_method; then if validate_track_method $track_method; then
$LOG warn "Using ping to track interface $INTERFACE avaliability" LOG warn "Using ping to track interface $INTERFACE avaliability"
else else
$LOG err "No track method avaliable" LOG err "No track method avaliable"
exit 1 exit 1
fi fi
} }
config_get reliability $1 reliability 1 config_get reliability $INTERFACE reliability 1
config_get count $1 count 1 config_get count $INTERFACE count 1
config_get timeout $1 timeout 4 config_get timeout $INTERFACE timeout 4
config_get interval $1 interval 10 config_get interval $INTERFACE interval 10
config_get down $1 down 5 config_get down $INTERFACE down 5
config_get up $1 up 5 config_get up $INTERFACE up 5
config_get size $1 size 56 config_get size $INTERFACE size 56
config_get max_ttl $1 max_ttl 60 config_get max_ttl $INTERFACE max_ttl 60
config_get failure_interval $1 failure_interval $interval config_get failure_interval $INTERFACE failure_interval $interval
config_get_bool keep_failure_interval $1 keep_failure_interval 0 config_get_bool keep_failure_interval $INTERFACE keep_failure_interval 0
config_get recovery_interval $1 recovery_interval $interval config_get recovery_interval $INTERFACE recovery_interval $interval
config_get_bool check_quality $1 check_quality 0 config_get_bool check_quality $INTERFACE check_quality 0
config_get failure_latency $1 failure_latency 1000 config_get failure_latency $INTERFACE failure_latency 1000
config_get recovery_latency $1 recovery_latency 500 config_get recovery_latency $INTERFACE recovery_latency 500
config_get failure_loss $1 failure_loss 40 config_get failure_loss $INTERFACE failure_loss 40
config_get recovery_loss $1 recovery_loss 10 config_get recovery_loss $INTERFACE recovery_loss 10
local score=$(($down+$up)) local score=$(($down+$up))
local track_ips=$(echo $* | cut -d ' ' -f 5-99) local track_ips=$(echo $* | cut -d ' ' -f 5-99)
local host_up_count=0 local host_up_count=0
local lost=0 local lost=0
local sleep_time=0
local turn=0 local turn=0
local result
local ping_protocol=4 local ping_protocol=4
local ping_result local sleep_time result ping_result ping_result_raw ping_status loss latency
local ping_result_raw
local ping_status
local loss=0
local latency=0
if [ "$STATUS" = "offline" ]; then firstconnect
echo "offline" > /var/run/mwan3track/$1/STATUS
echo "0" > /var/run/mwan3track/$1/ONLINE
echo "$(get_uptime)" > /var/run/mwan3track/$1/OFFLINE
score=0
else
echo "online" > /var/run/mwan3track/$1/STATUS
echo "0" > /var/run/mwan3track/$1/OFFLINE
echo "$(get_uptime)" > /var/run/mwan3track/$1/ONLINE
env -i ACTION="connected" INTERFACE="$1" DEVICE="$2" /sbin/hotplug-call iface
fi
while true; do while true; do
sleep_time=$interval sleep_time=$interval
@ -139,16 +171,16 @@ main() {
# pinging IPv6 hosts with an interface is troublesome # pinging IPv6 hosts with an interface is troublesome
# https://bugs.openwrt.org/index.php?do=details&task_id=2897 # https://bugs.openwrt.org/index.php?do=details&task_id=2897
# so get the IP address of the interface and use that instead # so get the IP address of the interface and use that instead
if echo $track_ip | grep -q ':'; then if [ -z ${track_ip##*:*} ]; then
ADDR=$(ip -6 addr ls dev "$DEVICE" | sed -ne '/\/128/d' -e 's/ *inet6 \([^ \/]*\).* scope global.*/\1/p' | head -n1)
[ -z "$ADDR" ] && ADDR=$(ip -6 addr ls dev "$DEVICE" | sed -ne 's/ *inet6 \([^ \/]*\).* scope global.*/\1/p')
ping_protocol=6 ping_protocol=6
else
unset SRC_IP
fi fi
if [ $check_quality -eq 0 ]; then if [ $check_quality -eq 0 ]; then
$PING -$ping_protocol -I ${ADDR:-$DEVICE} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null $PING -$ping_protocol -I ${SRC_IP:-$DEVICE} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null
result=$? result=$?
else else
ping_result_raw="$($PING -$ping_protocol -I ${ADDR:-$DEVICE} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null)" ping_result_raw="$($PING -$ping_protocol -I ${SRC_IP:-$DEVICE} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null)"
ping_status=$? ping_status=$?
ping_result=$(echo "$ping_result_raw" | tail -n2) ping_result=$(echo "$ping_result_raw" | tail -n2)
loss="$(echo "$ping_result" | grep "packet loss" | cut -d "," -f3 | awk '{print $1}' | sed -e 's/%//')" loss="$(echo "$ping_result" | grep "packet loss" | cut -d "," -f3 | awk '{print $1}' | sed -e 's/%//')"
@ -188,42 +220,40 @@ main() {
if [ $check_quality -eq 0 ]; then if [ $check_quality -eq 0 ]; then
if [ $result -eq 0 ]; then if [ $result -eq 0 ]; then
let host_up_count++ let host_up_count++
echo "up" > /var/run/mwan3track/$1/TRACK_${track_ip} update_status "$track_ip" "up"
if [ $score -le $up ]; then if [ $score -le $up ]; then
$LOG info "Check ($track_method) success for target \"$track_ip\" on interface $1 ($2)" LOG info "Check ($track_method) success for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi fi
else else
let lost++ let lost++
echo "down" > /var/run/mwan3track/$1/TRACK_${track_ip} update_status "$track_ip" "down"
if [ $score -gt $up ]; then if [ $score -gt $up ]; then
$LOG info "Check ($track_method) failed for target \"$track_ip\" on interface $1 ($2)" LOG info "Check ($track_method) failed for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi fi
fi fi
else else
if [ "$loss" -ge "$failure_loss" -o "$latency" -ge "$failure_latency" ]; then if [ "$loss" -ge "$failure_loss" -o "$latency" -ge "$failure_latency" ]; then
let lost++ let lost++
echo "down" > /var/run/mwan3track/$1/TRACK_${track_ip} update_status "$track_ip" "down" $latency $loss
echo "$latency" > /var/run/mwan3track/$1/LATENCY_${track_ip}
echo "$loss" > /var/run/mwan3track/$1/LOSS_${track_ip}
if [ $score -gt $up ]; then if [ $score -gt $up ]; then
$LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) failed for target \"$track_ip\" on interface $1 ($2)" LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) failed for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi fi
elif [ "$loss" -le "$recovery_loss" -a "$latency" -le "$recovery_latency" ]; then elif [ "$loss" -le "$recovery_loss" -a "$latency" -le "$recovery_latency" ]; then
let host_up_count++ let host_up_count++
echo "up" > /var/run/mwan3track/$1/TRACK_${track_ip} update_status "$track_ip" "up" $latency $loss
echo "$latency" > /var/run/mwan3track/$1/LATENCY_${track_ip}
echo "$loss" > /var/run/mwan3track/$1/LOSS_${track_ip}
if [ $score -le $up ]; then if [ $score -le $up ]; then
$LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) success for target \"$track_ip\" on interface $1 ($2)" LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) success for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi fi
else else
echo "skipped" > /var/run/mwan3track/$1/TRACK_${track_ip} echo "skipped" > /var/run/mwan3track/$INTERFACE/TRACK_${track_ip}
fi fi
fi fi
else else
echo "skipped" > /var/run/mwan3track/$1/TRACK_${track_ip} echo "skipped" > /var/run/mwan3track/$INTERFACE/TRACK_${track_ip}
fi fi
done done
@ -240,53 +270,50 @@ main() {
fi fi
if [ $score -eq $up ]; then if [ $score -eq $up ]; then
echo "offline" > /var/run/mwan3track/$1/STATUS disconnected
env -i ACTION=ifdown INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface
score=0 score=0
fi fi
else else
if [ $score -lt $(($down+$up)) ] && [ $lost -gt 0 ]; then if [ $score -lt $(($down+$up)) ] && [ $lost -gt 0 ]; then
$LOG info "Lost $(($lost*$count)) ping(s) on interface $1 ($2)" LOG info "Lost $(($lost*$count)) ping(s) on interface $INTERFACE ($DEVICE). Current score: $score"
fi fi
let score++ let score++
lost=0 lost=0
if [ $score -gt $up ]; then if [ $score -gt $up ]; then
echo "online" > /var/run/mwan3track/$1/STATUS echo "online" > /var/run/mwan3track/$INTERFACE/STATUS
score=$(($down+$up)) score=$(($down+$up))
elif [ $score -le $up ]; then elif [ $score -le $up ]; then
sleep_time=$recovery_interval sleep_time=$recovery_interval
fi fi
if [ $score -eq $up ]; then if [ $score -eq $up ]; then
$LOG notice "Interface $1 ($2) is online" connected $INTERFACE $DEVICE
echo "online" > /var/run/mwan3track/$1/STATUS
env -i ACTION=ifup INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface
exit 0
fi fi
fi fi
let turn++ let turn++
mkdir -p "/var/run/mwan3track/${1}" mkdir -p "/var/run/mwan3track/${1}"
echo "${lost}" > /var/run/mwan3track/$1/LOST echo "${lost}" > /var/run/mwan3track/$INTERFACE/LOST
echo "${score}" > /var/run/mwan3track/$1/SCORE echo "${score}" > /var/run/mwan3track/$INTERFACE/SCORE
echo "${turn}" > /var/run/mwan3track/$1/TURN echo "${turn}" > /var/run/mwan3track/$INTERFACE/TURN
echo "$(get_uptime)" > /var/run/mwan3track/$1/TIME echo "$(get_uptime)" > /var/run/mwan3track/$INTERFACE/TIME
host_up_count=0 host_up_count=0
sleep "${sleep_time}" & sleep "${sleep_time}" &
wait wait
if [ "${IFDOWN_EVENT}" -eq 1 ]; then if [ "${IFDOWN_EVENT}" -eq 1 ]; then
echo "offline" > /var/run/mwan3track/$1/STATUS LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})"
echo "$(get_uptime)" > /var/run/mwan3track/$1/OFFLINE disconnected 1
echo "0" > /var/run/mwan3track/$1/ONLINE
$LOG notice "Interface $1 ($2) is offline"
env -i ACTION="disconnected" INTERFACE="$1" DEVICE="$2" /sbin/hotplug-call iface
score=0
IFDOWN_EVENT=0 IFDOWN_EVENT=0
fi fi
if [ "${IFUP_EVENT}" -eq 1 ]; then
LOG debug "Register ifup event on interface ${INTERFACE} (${DEVICE})"
firstconnect
IFUP_EVENT=0
fi
done done
} }