Merge pull request #13169 from aaronjg/mwan3-owner-procd

mwan3: mwan3track via default routing table and use procd from mwan3track & mwan3rtmon
This commit is contained in:
Florian Eckert 2020-10-21 16:33:19 +02:00 committed by GitHub
commit 9485b9401d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1097 additions and 751 deletions

View file

@ -8,10 +8,11 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=mwan3 PKG_NAME:=mwan3
PKG_VERSION:=2.9.0 PKG_VERSION:=2.10.0
PKG_RELEASE:=1 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
PKG_CONFIG_DEPENDS:=CONFIG_IPV6
include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/package.mk
@ -61,8 +62,13 @@ fi
exit 0 exit 0
endef endef
define Build/Compile
$(TARGET_CC) $(CFLAGS) $(LDFLAGS) $(FPIC) -shared -o $(PKG_BUILD_DIR)/libwrap_mwan3_sockopt.so.1.0 $(if $(CONFIG_IPV6),-DCONFIG_IPV6) $(PKG_BUILD_DIR)/sockopt_wrap.c -ldl
endef
define Package/mwan3/install define Package/mwan3/install
$(CP) ./files/* $(1) $(CP) ./files/* $(1)
$(CP) $(PKG_BUILD_DIR)/libwrap_mwan3_sockopt.so.1.0 $(1)/lib/mwan3/
endef endef
$(eval $(call BuildPackage,mwan3)) $(eval $(call BuildPackage,mwan3))

View file

@ -1,3 +1,5 @@
# For full documentation of mwan3 configuration:
# https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3#mwan3_configuration
config globals 'globals' config globals 'globals'
option mmx_mask '0x3F00' option mmx_mask '0x3F00'
@ -10,15 +12,6 @@ config interface 'wan'
list track_ip '208.67.220.220' list track_ip '208.67.220.220'
option family 'ipv4' option family 'ipv4'
option reliability '2' option reliability '2'
option count '1'
option timeout '2'
option failure_latency '1000'
option recovery_latency '500'
option failure_loss '20'
option recovery_loss '5'
option interval '5'
option down '3'
option up '8'
config interface 'wan6' config interface 'wan6'
option enabled '0' option enabled '0'
@ -28,11 +21,6 @@ config interface 'wan6'
list track_ip '2620:0:ccc::2' list track_ip '2620:0:ccc::2'
option family 'ipv6' option family 'ipv6'
option reliability '2' option reliability '2'
option count '1'
option timeout '2'
option interval '5'
option down '3'
option up '8'
config interface 'wanb' config interface 'wanb'
option enabled '0' option enabled '0'
@ -42,15 +30,6 @@ config interface 'wanb'
list track_ip '208.67.220.220' list track_ip '208.67.220.220'
option family 'ipv4' option family 'ipv4'
option reliability '1' option reliability '1'
option count '1'
option timeout '2'
option failure_latency '1000'
option recovery_latency '500'
option failure_loss '20'
option recovery_loss '5'
option interval '5'
option down '3'
option up '8'
config interface 'wanb6' config interface 'wanb6'
option enabled '0' option enabled '0'
@ -60,11 +39,6 @@ config interface 'wanb6'
list track_ip '2620:0:ccc::2' list track_ip '2620:0:ccc::2'
option family 'ipv6' option family 'ipv6'
option reliability '1' option reliability '1'
option count '1'
option timeout '2'
option interval '5'
option down '3'
option up '8'
config member 'wan_m1_w3' config member 'wan_m1_w3'
option interface 'wan' option interface 'wan'

View file

@ -9,7 +9,9 @@
SCRIPTNAME="mwan3-hotplug" SCRIPTNAME="mwan3-hotplug"
[ "$ACTION" = "ifup" ] || [ "$ACTION" = "ifdown" ] || [ "$ACTION" = "connected" ] || [ "$ACTION" = "disconnected" ] || exit 1 [ "$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 [ "$FIRSTCONNECT" = "1" ] || [ "$MWAN3_SHUTDOWN" = "1" ] && exit 0
if { [ "$ACTION" = "ifup" ] || [ "$ACTION" = "connected" ] ; } && [ -z "$DEVICE" ]; then
LOG notice "$ACTION called on $INTERFACE with no device set" LOG notice "$ACTION called on $INTERFACE with no device set"
exit 3 exit 3
fi fi
@ -17,10 +19,9 @@ fi
[ "$MWAN3_STARTUP" = 1 ] || mwan3_lock "$ACTION" "$INTERFACE" [ "$MWAN3_STARTUP" = 1 ] || mwan3_lock "$ACTION" "$INTERFACE"
config_load mwan3 config_load mwan3
config_get_bool enabled globals 'enabled' '0' /etc/init.d/mwan3 running || {
[ "${enabled}" -gt 0 ] || {
[ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE"
LOG notice "mwan3 hotplug on $INTERFACE not called because globally disabled" LOG notice "mwan3 hotplug $ACTION on $INTERFACE not called because globally disabled"
mwan3_flush_conntrack "$INTERFACE" "$ACTION" mwan3_flush_conntrack "$INTERFACE" "$ACTION"
exit 0 exit 0
} }
@ -33,15 +34,14 @@ $IPT4 -S mwan3_hook &>/dev/null || {
mwan3_init mwan3_init
[ "$MWAN3_STARTUP" = 1 ] || { [ "$MWAN3_STARTUP" = 1 ] || {
mwan3_set_connected_iptables config_get family $INTERFACE family ipv4
mwan3_set_custom_ipset mwan3_set_connected_${family}
} }
if [ "$MWAN3_STARTUP" != 1 ]; then if [ "$MWAN3_STARTUP" != 1 ] && [ "$ACTION" = "ifup" ]; then
mwan3_set_user_iface_rules $INTERFACE $DEVICE mwan3_set_user_iface_rules $INTERFACE $DEVICE
fi fi
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_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE" [ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$INTERFACE"
@ -49,54 +49,47 @@ config_get_bool enabled $INTERFACE 'enabled' '0'
exit 0 exit 0
} }
trackpid=$(pgrep -f "mwan3track $INTERFACE ") config_get initial_state $INTERFACE initial_state "online"
if [ "$initial_state" = "offline" ]; then if [ "$initial_state" = "offline" ]; then
status=$(cat $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS 2>/dev/null || echo unknown) status=$(cat $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS 2>/dev/null || echo unknown)
[ "$status" = "online" ] || status=offline
else else
status=online status=online
fi fi
[ -z "$TRUE_INTERFACE" ] && mwan3_get_true_iface TRUE_INTERFACE $INTERFACE if [ "$ACTION" = ifup ] || [ "$ACTION" = ifdown ]; then
initscript=/etc/init.d/mwan3
. /lib/functions/procd.sh
fi
binary_status=$status LOG notice "Execute $ACTION event on interface $INTERFACE (${DEVICE:-unknown})"
[ "$binary_status" = "online" ] || binary_status=offline
LOG notice "Execute "$ACTION" event on interface $INTERFACE (${DEVICE:-unknown})"
case "$ACTION" in case "$ACTION" in
ifup|connected) connected)
mwan3_set_iface_hotplug_state $INTERFACE "online"
mwan3_set_policies_iptables
;;
ifup)
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_set_iface_hotplug_state $INTERFACE "$status"
if [ "$MWAN3_STARTUP" != 1 ]; then
mwan3_create_iface_route $INTERFACE $DEVICE mwan3_create_iface_route $INTERFACE $DEVICE
[ "$MWAN3_STARTUP" != 1 ] && mwan3_add_non_default_iface_route $INTERFACE $DEVICE [ "$status" = "online" ] && mwan3_set_policies_iptables
mwan3_set_iface_hotplug_state $INTERFACE "$binary_status"
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
mwan3_track $INTERFACE $DEVICE "$binary_status" "$src_ip"
LOG notice "Restarted tracker [$!] on interface $INTERFACE (${DEVICE:-unknown})"
fi fi
else [ "$ACTION" = ifup ] && procd_running mwan3 "track_$INTERFACE" && procd_send_signal mwan3 "track_$INTERFACE" USR2
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|disconnected) disconnected)
mwan3_set_iface_hotplug_state $INTERFACE "offline"
mwan3_set_policies_iptables
;;
ifdown)
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_delete_iface_rules $INTERFACE mwan3_delete_iface_rules $INTERFACE
mwan3_delete_iface_route $INTERFACE mwan3_delete_iface_route $INTERFACE
mwan3_delete_iface_iptables $INTERFACE mwan3_delete_iface_iptables $INTERFACE
if [ "$ACTION" = "ifdown" ]; then procd_running mwan3 "track_$INTERFACE" && procd_send_signal mwan3 "track_$INTERFACE" USR1
[ -n "$trackpid" ] && kill -USR1 "$trackpid"
fi
mwan3_set_policies_iptables mwan3_set_policies_iptables
;; ;;
esac esac

View file

@ -4,22 +4,22 @@
. /lib/functions.sh . /lib/functions.sh
. /lib/mwan3/mwan3.sh . /lib/mwan3/mwan3.sh
[ "$MWAN3_STARTUP" = 1 ] || mwan3_lock "$ACTION" "$DEVICE-user" [ "$MWAN3_SHUTDOWN" != 1 ] && mwan3_lock "$ACTION" "$DEVICE-user"
config_load mwan3 [ "$MWAN3_SHUTDOWN" != 1 ] && ! /etc/init.d/mwan3 running && {
config_get_bool enabled globals 'enabled' '0' mwan3_unlock "$ACTION" "$DEVICE-user"
[ "${enabled}" -gt 0 ] || {
[ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$DEVICE-user"
exit 0 exit 0
} }
config_load mwan3
config_get_bool enabled "$INTERFACE" enabled 0 config_get_bool enabled "$INTERFACE" enabled 0
[ "${enabled}" -eq 1 ] || { [ "${enabled}" -eq 1 ] || {
[ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$DEVICE-user" [ "$MWAN3_SHUTDOWN" != 1 ] && mwan3_unlock "$ACTION" "$DEVICE-user"
exit 0 exit 0
} }
[ "$MWAN3_STARTUP" = 1 ] || mwan3_unlock "$ACTION" "$DEVICE-user" [ "$MWAN3_SHUTDOWN" != 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

@ -1,31 +1,114 @@
#!/bin/sh /etc/rc.common #!/bin/sh /etc/rc.common
. /lib/functions.sh
. /lib/mwan3/common.sh
. /lib/functions/network.sh
. /lib/mwan3/mwan3.sh
START=19 START=19
USE_PROCD=1 USE_PROCD=1
boot() { service_running() {
. /lib/config/uci.sh [ -d "$MWAN3_STATUS_DIR" ]
# disabled until mwan3 start runs so hotplug scripts
# do not start prematurely
uci_toggle_state mwan3 globals enabled "0"
rc_procd start_service
} }
# FIXME start_tracker() {
# fd 1000 is an inherited lock file descriptor for preventing concurrent local enabled interface
# init script executions. Close it here to prevent the mwan3 daemon from interface=$1
# inheriting it further to avoid holding the lock indefinitely. config_get_bool enabled $interface 'enabled' '0'
[ $enabled -eq 0 ] && return
reload_service() { procd_open_instance "track_${1}"
/usr/sbin/mwan3 restart 1000>&- procd_set_param command /usr/sbin/mwan3track $interface
procd_set_param respawn
procd_close_instance
} }
start_service() { start_service() {
/usr/sbin/mwan3 start 1000>&- local enabled hotplug_pids
config_load mwan3
mwan3_init
config_foreach start_tracker interface
mwan3_lock "command" "mwan3"
mwan3_update_iface_to_table
mwan3_set_connected_ipset
mwan3_set_custom_ipset
mwan3_set_general_rules
mwan3_set_general_iptables
config_foreach mwan3_ifup interface 1
wait $hotplug_pids
mwan3_set_policies_iptables
mwan3_set_user_rules
mwan3_unlock "command" "mwan3"
procd_open_instance rtmon_ipv4
procd_set_param command /usr/sbin/mwan3rtmon ipv4
procd_set_param respawn
procd_close_instance
if command -v ip6tables > /dev/null; then
procd_open_instance rtmon_ipv6
procd_set_param command /usr/sbin/mwan3rtmon ipv6
procd_set_param respawn
procd_close_instance
fi
} }
stop_service() { stop_service() {
/usr/sbin/mwan3 stop 1000>&- local ipset rule IP IPTR IPT family table tid
mwan3_lock "command" "mwan3"
config_load mwan3
mwan3_init
config_foreach mwan3_interface_shutdown interface
for family in ipv4 ipv6; do
if [ "$family" = "ipv4" ]; then
IPT="$IPT4"
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
for rule in $($IP rule list | grep -E '^[1-3][0-9]{3}\:' | cut -d ':' -f 1); do
$IP rule del pref $rule &> /dev/null
done
table="$($IPT -S)"
{
echo "*mangle";
[ -z "${table##*PREROUTING -j mwan3_hook*}" ] && echo "-D PREROUTING -j mwan3_hook"
[ -z "${table##*OUTPUT -j mwan3_hook*}" ] && echo "-D OUTPUT -j mwan3_hook"
echo "$table" | awk '{print "-F "$2}' | grep mwan3 | sort -u
echo "$table" | awk '{print "-X "$2}' | grep mwan3 | sort -u
echo "COMMIT"
} | $IPTR
done
for ipset in $($IPS -n list | grep mwan3_); do
$IPS -q destroy $ipset
done
for ipset in $($IPS -n list | grep mwan3 | grep -E '_v4|_v6'); do
$IPS -q destroy $ipset
done
rm -rf $MWAN3_STATUS_DIR $MWAN3TRACK_STATUS_DIR
mwan3_unlock "command" "mwan3"
} }
service_triggers() { service_triggers() {

View file

@ -5,7 +5,24 @@ get_uptime() {
echo "${uptime%%.*}" echo "${uptime%%.*}"
} }
IP4="ip -4"
IP6="ip -6"
SCRIPTNAME="$(basename "$0")" SCRIPTNAME="$(basename "$0")"
MWAN3_STATUS_DIR="/var/run/mwan3"
MWAN3TRACK_STATUS_DIR="/var/run/mwan3track"
MWAN3_INTERFACE_MAX=""
MMX_MASK=""
MMX_DEFAULT=""
MMX_BLACKHOLE=""
MM_BLACKHOLE=""
MMX_UNREACHABLE=""
MM_UNREACHABLE=""
MAX_SLEEP=$(((1<<31)-1))
LOG() LOG()
{ {
local facility=$1; shift local facility=$1; shift
@ -13,5 +30,150 @@ LOG()
# when this release is out of beta, the comment in the line below # when this release is out of beta, the comment in the line below
# should be removed # should be removed
[ "$facility" = "debug" ] && return [ "$facility" = "debug" ] && return
logger -t "$SCRIPTNAME[$$]" -p $facility "$*" logger -t "${SCRIPTNAME}[$$]" -p $facility "$*"
}
mwan3_get_true_iface()
{
local family V
_true_iface=$2
config_get family "$2" family ipv4
if [ "$family" = "ipv4" ]; then
V=4
elif [ "$family" = "ipv6" ]; then
V=6
fi
ubus call "network.interface.${2}_${V}" status &>/dev/null && _true_iface="${2}_${V}"
export "$1=$_true_iface"
}
mwan3_get_src_ip()
{
local family _src_ip interface true_iface device addr_cmd default_ip IP sed_str
interface=$2
mwan3_get_true_iface true_iface $interface
unset "$1"
config_get family "$interface" family ipv4
if [ "$family" = "ipv4" ]; then
addr_cmd='network_get_ipaddr'
default_ip="0.0.0.0"
sed_str='s/ *inet \([^ \/]*\).*/\1/;T; pq'
IP="$IP4"
elif [ "$family" = "ipv6" ]; then
addr_cmd='network_get_ipaddr6'
default_ip="::"
sed_str='s/ *inet6 \([^ \/]*\).* scope.*/\1/;T; pq'
IP="$IP6"
fi
$addr_cmd _src_ip "$true_iface"
if [ -z "$_src_ip" ]; then
network_get_device device $true_iface
_src_ip=$($IP address ls dev $device 2>/dev/null | sed -ne "$sed_str")
if [ -n "$_src_ip" ]; then
LOG warn "no src $family address found from netifd for interface '$true_iface' dev '$device' guessing $_src_ip"
else
_src_ip="$default_ip"
LOG warn "no src $family address found for interface '$true_iface' dev '$device'"
fi
fi
export "$1=$_src_ip"
}
mwan3_get_mwan3track_status()
{
local track_ips pid
mwan3_list_track_ips()
{
track_ips="$1 $track_ips"
}
config_list_foreach "$1" track_ip mwan3_list_track_ips
if [ -n "$track_ips" ]; then
pid="$(pgrep -f "mwan3track $1$")"
if [ -n "$pid" ]; then
if [ "$(cat /proc/"$(pgrep -P $pid)"/cmdline)" = "sleep${MAX_SLEEP}" ]; then
tracking="paused"
else
tracking="active"
fi
else
tracking="down"
fi
else
tracking="not enabled"
fi
echo "$tracking"
}
mwan3_init()
{
local bitcnt
local mmdefault
[ -d $MWAN3_STATUS_DIR ] || mkdir -p $MWAN3_STATUS_DIR/iface_state
# mwan3's MARKing mask (at least 3 bits should be set)
if [ -e "${MWAN3_STATUS_DIR}/mmx_mask" ]; then
MMX_MASK=$(cat "${MWAN3_STATUS_DIR}/mmx_mask")
MWAN3_INTERFACE_MAX=$(uci_get_state mwan3 globals iface_max)
else
config_load mwan3
config_get MMX_MASK globals mmx_mask '0x3F00'
echo "$MMX_MASK"| tr 'A-F' 'a-f' > "${MWAN3_STATUS_DIR}/mmx_mask"
LOG debug "Using firewall mask ${MMX_MASK}"
bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MWAN3_INTERFACE_MAX=$((mmdefault-3))
uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX"
LOG debug "Max interface count is ${MWAN3_INTERFACE_MAX}"
fi
# mark mask constants
bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MM_BLACKHOLE=$((mmdefault-2))
MM_UNREACHABLE=$((mmdefault-1))
# MMX_DEFAULT should equal MMX_MASK
MMX_DEFAULT=$(mwan3_id2mask mmdefault MMX_MASK)
MMX_BLACKHOLE=$(mwan3_id2mask MM_BLACKHOLE MMX_MASK)
MMX_UNREACHABLE=$(mwan3_id2mask MM_UNREACHABLE MMX_MASK)
}
# maps the 1st parameter so it only uses the bits allowed by the bitmask (2nd parameter)
# which means spreading the bits of the 1st parameter to only use the bits that are set to 1 in the 2nd parameter
# 0 0 0 0 0 1 0 1 (0x05) 1st parameter
# 1 0 1 0 1 0 1 0 (0xAA) 2nd parameter
# 1 0 1 result
mwan3_id2mask()
{
local bit_msk bit_val result
bit_val=0
result=0
for bit_msk in $(seq 0 31); do
if [ $((($2>>bit_msk)&1)) = "1" ]; then
if [ $((($1>>bit_val)&1)) = "1" ]; then
result=$((result|(1<<bit_msk)))
fi
bit_val=$((bit_val+1))
fi
done
printf "0x%x" $result
}
# counts how many bits are set to 1
# n&(n-1) clears the lowest bit set to 1
mwan3_count_one_bits()
{
local count n
count=0
n=$(($1))
while [ "$n" -gt "0" ]; do
n=$((n&(n-1)))
count=$((count+1))
done
echo $count
} }

View file

@ -2,8 +2,6 @@
. /usr/share/libubox/jshn.sh . /usr/share/libubox/jshn.sh
IP4="ip -4"
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"
@ -22,18 +20,9 @@ IPv6_REGEX="${IPv6_REGEX}:((:[0-9a-fA-F]{1,4}){1,7}|:)|"
IPv6_REGEX="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|" IPv6_REGEX="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
IPv6_REGEX="${IPv6_REGEX}::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|" IPv6_REGEX="${IPv6_REGEX}::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])" IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
IPv4_REGEX="((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
MWAN3_STATUS_DIR="/var/run/mwan3"
MWAN3TRACK_STATUS_DIR="/var/run/mwan3track"
MWAN3_INTERFACE_MAX=""
DEFAULT_LOWEST_METRIC=256 DEFAULT_LOWEST_METRIC=256
MMX_MASK=""
MMX_DEFAULT=""
MMX_BLACKHOLE=""
MM_BLACKHOLE=""
MMX_UNREACHABLE=""
MM_UNREACHABLE=""
command -v ip6tables > /dev/null command -v ip6tables > /dev/null
NO_IPV6=$? NO_IPV6=$?
@ -43,14 +32,15 @@ mwan3_push_update()
# helper function to build an update string to pass on to # helper function to build an update string to pass on to
# IPTR or IPS RESTORE. Modifies the 'update' variable in # IPTR or IPS RESTORE. Modifies the 'update' variable in
# the local scope. # the local scope.
update="$update update="$update"$'\n'"$*";
$*";
} }
mwan3_update_dev_to_table() mwan3_update_dev_to_table()
{ {
local _tid local _tid
# shellcheck disable=SC2034
mwan3_dev_tbl_ipv4=" " mwan3_dev_tbl_ipv4=" "
# shellcheck disable=SC2034
mwan3_dev_tbl_ipv6=" " mwan3_dev_tbl_ipv6=" "
update_table() update_table()
@ -81,20 +71,6 @@ mwan3_update_iface_to_table()
config_foreach update_table interface config_foreach update_table interface
} }
mwan3_get_true_iface()
{
local family V
_true_iface=$2
config_get family "$iface" family ipv4
if [ "$family" = "ipv4" ]; then
V=4
elif [ "$family" = "ipv6" ]; then
V=6
fi
ubus call "network.interface.${iface}_${V}" status &>/dev/null && _true_iface="${iface}_${V}"
export "$1=$_true_iface"
}
mwan3_route_line_dev() mwan3_route_line_dev()
{ {
# must have mwan3 config already loaded # must have mwan3 config already loaded
@ -130,63 +106,6 @@ mwan3_count_one_bits()
echo $count echo $count
} }
# maps the 1st parameter so it only uses the bits allowed by the bitmask (2nd parameter)
# which means spreading the bits of the 1st parameter to only use the bits that are set to 1 in the 2nd parameter
# 0 0 0 0 0 1 0 1 (0x05) 1st parameter
# 1 0 1 0 1 0 1 0 (0xAA) 2nd parameter
# 1 0 1 result
mwan3_id2mask()
{
local bit_msk bit_val result
bit_val=0
result=0
for bit_msk in $(seq 0 31); do
if [ $((($2>>bit_msk)&1)) = "1" ]; then
if [ $((($1>>bit_val)&1)) = "1" ]; then
result=$((result|(1<<bit_msk)))
fi
bit_val=$((bit_val+1))
fi
done
printf "0x%x" $result
}
mwan3_init()
{
local bitcnt
local mmdefault
[ -d $MWAN3_STATUS_DIR ] || mkdir -p $MWAN3_STATUS_DIR/iface_state
# mwan3's MARKing mask (at least 3 bits should be set)
if [ -e "${MWAN3_STATUS_DIR}/mmx_mask" ]; then
MMX_MASK=$(cat "${MWAN3_STATUS_DIR}/mmx_mask")
MWAN3_INTERFACE_MAX=$(uci_get_state mwan3 globals iface_max)
else
config_load mwan3
config_get MMX_MASK globals mmx_mask '0x3F00'
echo "$MMX_MASK"| tr 'A-F' 'a-f' > "${MWAN3_STATUS_DIR}/mmx_mask"
LOG debug "Using firewall mask ${MMX_MASK}"
bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MWAN3_INTERFACE_MAX=$(($mmdefault-3))
uci_toggle_state mwan3 globals iface_max "$MWAN3_INTERFACE_MAX"
LOG debug "Max interface count is ${MWAN3_INTERFACE_MAX}"
fi
# mark mask constants
bitcnt=$(mwan3_count_one_bits MMX_MASK)
mmdefault=$(((1<<bitcnt)-1))
MM_BLACKHOLE=$(($mmdefault-2))
MM_UNREACHABLE=$(($mmdefault-1))
# MMX_DEFAULT should equal MMX_MASK
MMX_DEFAULT=$(mwan3_id2mask mmdefault MMX_MASK)
MMX_BLACKHOLE=$(mwan3_id2mask MM_BLACKHOLE MMX_MASK)
MMX_UNREACHABLE=$(mwan3_id2mask MM_UNREACHABLE MMX_MASK)
}
mwan3_lock() { mwan3_lock() {
lock /var/run/mwan3.lock lock /var/run/mwan3.lock
#LOG debug "$1 $2 (lock)" #LOG debug "$1 $2 (lock)"
@ -197,22 +116,6 @@ mwan3_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
@ -220,14 +123,13 @@ 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()
{ {
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}' | grep -E "$IPv4_REGEX"); 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"
mwan3_push_update -! add mwan3_custom_v4 "$custom_network_v4" mwan3_push_update -! add mwan3_custom_v4 "$custom_network_v4"
done done
@ -237,7 +139,7 @@ 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}' | grep -E "$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"
mwan3_push_update -! add mwan3_custom_v6 "$custom_network_v6" mwan3_push_update -! add mwan3_custom_v6 "$custom_network_v6"
done done
@ -263,7 +165,6 @@ mwan3_set_custom_ipset()
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 hash:net
$IPS create mwan3_connected_v4_temp hash:net $IPS create mwan3_connected_v4_temp hash:net
@ -274,7 +175,7 @@ mwan3_set_connected_ipv4()
$IP4 route | awk '{print $1}' $IP4 route | awk '{print $1}'
$IP4 route list table 0 | awk '{print $2}' $IP4 route list table 0 | awk '{print $2}'
} }
for connected_network_v4 in $(route_lists | egrep "$ipv4regex"); do for connected_network_v4 in $(route_lists | grep -E "$IPv4_REGEX"); do
if [ -z "${connected_network_v4##*/*}" ]; then if [ -z "${connected_network_v4##*/*}" ]; then
cidr_list="$cidr_list $connected_network_v4" cidr_list="$cidr_list $connected_network_v4"
else else
@ -294,40 +195,44 @@ mwan3_set_connected_ipv4()
$IPS swap mwan3_connected_v4_temp mwan3_connected_v4 $IPS swap mwan3_connected_v4_temp mwan3_connected_v4
$IPS destroy mwan3_connected_v4_temp $IPS destroy mwan3_connected_v4_temp
$IPS -! add mwan3_connected mwan3_connected_v4
} }
mwan3_set_connected_iptables() mwan3_set_connected_ipv6()
{ {
local connected_network_v6 source_network_v6 error local connected_network_v6 error
local update="" local update=""
mwan3_set_connected_ipv4 [ $NO_IPV6 -eq 0 ] || return
[ $NO_IPV6 -eq 0 ] && {
mwan3_push_update -! create mwan3_connected_v6 hash:net family inet6 mwan3_push_update -! create mwan3_connected_v6 hash:net family inet6
mwan3_push_update flush mwan3_connected_v6 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}' | grep -E "$IPv6_REGEX"); do
mwan3_push_update -! add mwan3_connected_v6 "$connected_network_v6" mwan3_push_update -! add mwan3_connected_v6 "$connected_network_v6"
done done
mwan3_push_update -! create mwan3_source_v6 hash:net family inet6 mwan3_push_update -! add mwan3_connected mwan3_connected_v6
for source_network_v6 in $($IP6 addr ls | sed -ne 's/ *inet6 \([^ \/]*\).* scope global.*/\1/p'); do error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipv6: $error"
mwan3_push_update -! add mwan3_source_v6 "$source_network_v6"
done
} }
mwan3_set_connected_ipset()
{
local error
local update=""
mwan3_push_update -! create mwan3_connected list:set mwan3_push_update -! create mwan3_connected list:set
mwan3_push_update flush mwan3_connected mwan3_push_update flush mwan3_connected
mwan3_push_update -! add mwan3_connected mwan3_connected_v4
[ $NO_IPV6 -eq 0 ] && mwan3_push_update -! add mwan3_connected mwan3_connected_v6
mwan3_push_update -! create mwan3_dynamic_v4 hash:net mwan3_push_update -! create mwan3_dynamic_v4 hash:net
mwan3_push_update -! add mwan3_connected mwan3_dynamic_v4 mwan3_push_update -! add mwan3_connected mwan3_dynamic_v4
[ $NO_IPV6 -eq 0 ] && mwan3_push_update -! create mwan3_dynamic_v6 hash:net family inet6 if [ $NO_IPV6 -eq 0 ]; then
[ $NO_IPV6 -eq 0 ] && mwan3_push_update -! add mwan3_connected mwan3_dynamic_v6 mwan3_push_update -! create mwan3_dynamic_v6 hash:net family inet6
error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_iptables: $error" mwan3_push_update -! add mwan3_connected mwan3_dynamic_v6
fi
error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipset: $error"
} }
mwan3_set_general_rules() mwan3_set_general_rules()
@ -336,12 +241,12 @@ mwan3_set_general_rules()
for IP in "$IP4" "$IP6"; do for IP in "$IP4" "$IP6"; do
[ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue [ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue
RULE_NO=$(($MM_BLACKHOLE+2000)) RULE_NO=$((MM_BLACKHOLE+2000))
if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
$IP rule add pref $RULE_NO fwmark $MMX_BLACKHOLE/$MMX_MASK blackhole $IP rule add pref $RULE_NO fwmark $MMX_BLACKHOLE/$MMX_MASK blackhole
fi fi
RULE_NO=$(($MM_UNREACHABLE+2000)) RULE_NO=$((MM_UNREACHABLE+2000))
if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then if [ -z "$($IP rule list | awk -v var="$RULE_NO:" '$1 == var')" ]; then
$IP rule add pref $RULE_NO fwmark $MMX_UNREACHABLE/$MMX_MASK unreachable $IP rule add pref $RULE_NO fwmark $MMX_UNREACHABLE/$MMX_MASK unreachable
fi fi
@ -353,7 +258,7 @@ mwan3_set_general_iptables()
local IPT current update error 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
current="$($IPT -S)" current="$($IPT -S)"$'\n'
update="*mangle" update="*mangle"
if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
mwan3_push_update -N mwan3_ifaces_in mwan3_push_update -N mwan3_ifaces_in
@ -395,15 +300,10 @@ mwan3_set_general_iptables()
-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
mwan3_push_update -A mwan3_hook \
-m set --match-set mwan3_source_v6 src \
-p ipv6-icmp \
-m icmp6 --icmpv6-type 128 \
-j RETURN
fi fi
mwan3_push_update -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \
-j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" -j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK"
mwan3_push_update -A mwan3_hook \ mwan3_push_update -A mwan3_hook \
-m mark --mark 0x0/$MMX_MASK \ -m mark --mark 0x0/$MMX_MASK \
@ -439,7 +339,7 @@ mwan3_set_general_iptables()
mwan3_create_iface_iptables() mwan3_create_iface_iptables()
{ {
local id family connected_name IPT IPTR current update error local id family 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"
@ -447,26 +347,22 @@ mwan3_create_iface_iptables()
[ -n "$id" ] || return 0 [ -n "$id" ] || return 0
if [ "$family" = "ipv4" ]; then if [ "$family" = "ipv4" ]; then
connected_name=mwan3_connected
IPT="$IPT4" IPT="$IPT4"
IPTR="$IPT4R" IPTR="$IPT4R"
$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
IPT="$IPT6" IPT="$IPT6"
IPTR="$IPT6R" IPTR="$IPT6R"
$IPS -! create $connected_name hash:net family inet6
else else
return return
fi fi
current="$($IPT -S)"
current="$($IPT -S)"$'\n'
update="*mangle" update="*mangle"
if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then
mwan3_push_update -N mwan3_ifaces_in mwan3_push_update -N mwan3_ifaces_in
fi fi
if [ -n "${current##*-N mwan3_iface_in_$1*}" ]; then if [ -n "${current##*-N mwan3_iface_in_$1$'\n'*}" ]; then
mwan3_push_update -N "mwan3_iface_in_$1" mwan3_push_update -N "mwan3_iface_in_$1"
else else
mwan3_push_update -F "mwan3_iface_in_$1" mwan3_push_update -F "mwan3_iface_in_$1"
@ -474,17 +370,17 @@ mwan3_create_iface_iptables()
mwan3_push_update -A "mwan3_iface_in_$1" \ mwan3_push_update -A "mwan3_iface_in_$1" \
-i "$2" \ -i "$2" \
-m set --match-set $connected_name src \ -m set --match-set mwan3_connected 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"
mwan3_push_update -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"
if [ -n "${current##*-A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_${1}*}" ]; then if [ -n "${current##*-A mwan3_ifaces_in -m mark --mark 0x0/$MMX_MASK -j mwan3_iface_in_${1}$'\n'*}" ]; then
mwan3_push_update -A mwan3_ifaces_in \ mwan3_push_update -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"
@ -521,45 +417,17 @@ mwan3_delete_iface_iptables()
} }
mwan3_create_iface_route() mwan3_get_routes()
{ {
local id via metric V V_ IP family local source_routing
local iface device cmd true_iface config_get_bool source_routing globals source_routing 0
[ $source_routing -eq 0 ] && unset source_routing
iface=$1 $IP route list table main | sed -ne "/^linkdown/T; s/expires \([0-9]\+\)sec//;s/error [0-9]\+//; ${source_routing:+s/default\(.*\) from [^ ]*/default\1/;} p" | uniq
device=$2
config_get family "$iface" family ipv4
mwan3_get_iface_id id "$iface"
[ -n "$id" ] || return 0
mwan3_get_true_iface true_iface $iface
if [ "$family" = "ipv4" ]; then
V_=""
IP="$IP4"
elif [ "$family" = "ipv6" ]; then
V_=6
IP="$IP6"
fi
network_get_gateway${V_} via "$true_iface"
{ [ -z "$via" ] || [ "$via" = "0.0.0.0" ] || [ "$via" = "::" ] ; } && unset via
network_get_metric metric "$true_iface"
$IP route flush table "$id"
cmd="$IP route add table $id default \
${via:+via} $via \
${metric:+metric} $metric \
dev $2"
$cmd || LOG warn "ip cmd failed $cmd"
} }
mwan3_add_non_default_iface_route() mwan3_create_iface_route()
{ {
local tid route_line family IP id local tid route_line family IP id tbl
config_get family "$1" family ipv4 config_get family "$1" family ipv4
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
@ -571,10 +439,15 @@ mwan3_add_non_default_iface_route()
IP="$IP6" IP="$IP6"
fi fi
tbl=$($IP route list table $id 2>/dev/null)$'\n'
mwan3_update_dev_to_table mwan3_update_dev_to_table
$IP route list table main | grep -v "^default\|linkdown\|^::/0\|^fe80::/64\|^unreachable" | while read route_line; do mwan3_get_routes | while read -r route_line; do
mwan3_route_line_dev "tid" "$route_line" "$family" mwan3_route_line_dev "tid" "$route_line" "$family"
{ [ -z "${route_line##default*}" ] || [ -z "${route_line##fe80::/64*}" ]; } && [ "$tid" != "$id" ] && continue
if [ -z "$tid" ] || [ "$tid" = "$id" ]; then if [ -z "$tid" ] || [ "$tid" = "$id" ]; then
# possible that routes are already in the table
# if 'connected' was called after 'ifup'
[ -n "$tbl" ] && [ -z "${tbl##*$route_line$'\n'*}" ] && continue
$IP route add table $id $route_line || $IP route add table $id $route_line ||
LOG warn "failed to add $route_line to table $id" LOG warn "failed to add $route_line to table $id"
fi fi
@ -582,63 +455,21 @@ mwan3_add_non_default_iface_route()
done done
} }
mwan3_add_all_nondefault_routes()
{
local tid IP route_line ipv family active_tbls tid
add_active_tbls()
{
let tid++
config_get family "$1" family ipv4
[ "$family" != "$ipv" ] && return
$IP route list table $tid 2>/dev/null | grep -q "^default\|^::/0" && {
active_tbls="$active_tbls${tid} "
}
}
add_route()
{
let tid++
[ -n "${active_tbls##* $tid *}" ] && return
$IP route add table $tid $route_line ||
LOG warn "failed to add $route_line to table $tid"
}
mwan3_update_dev_to_table
for ipv in ipv4 ipv6; do
[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
if [ "$ipv" = "ipv4" ]; then
IP="$IP4"
elif [ "$ipv" = "ipv6" ]; then
IP="$IP6"
fi
tid=0
active_tbls=" "
config_foreach add_active_tbls interface
$IP route list table main | grep -v "^default\|linkdown\|^::/0\|^fe80::/64\|^unreachable" | while read route_line; do
mwan3_route_line_dev "tid" "$route_line" "$ipv"
if [ -n "$tid" ]; then
$IP route add table $tid $route_line
else
config_foreach add_route interface
fi
done
done
}
mwan3_delete_iface_route() mwan3_delete_iface_route()
{ {
local id local id family
config_get family "$1" family ipv4 config_get family "$1" family ipv4
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
[ -n "$id" ] || return 0 if [ -z "$id" ]; then
LOG warn "delete_iface_route: could not find table id for interface $1"
return 0
fi
if [ "$family" = "ipv4" ]; then if [ "$family" = "ipv4" ]; then
$IP4 route flush table "$id" $IP4 route flush table "$id"
fi elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
$IP6 route flush table "$id" $IP6 route flush table "$id"
fi fi
} }
@ -660,21 +491,16 @@ mwan3_create_iface_rules()
return return
fi fi
while [ -n "$($IP rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do mwan3_delete_iface_rules "$1"
$IP rule del pref $(($id+1000))
done
while [ -n "$($IP rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do $IP rule add pref $((id+1000)) iif "$2" lookup "$id"
$IP rule del pref $(($id+2000)) $IP rule add pref $((id+2000)) fwmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" lookup "$id"
done $IP rule add pref $((id+3000)) fwmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" unreachable
$IP rule add pref $(($id+1000)) iif "$2" lookup "$id"
$IP rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup "$id"
} }
mwan3_delete_iface_rules() mwan3_delete_iface_rules()
{ {
local id family local id family IP rule_id
config_get family "$1" family ipv4 config_get family "$1" family ipv4
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
@ -689,12 +515,8 @@ mwan3_delete_iface_rules()
return return
fi fi
while [ -n "$($IP rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do for rule_id in $(ip rule list | awk '$1 % 1000 == '$id' && $1 > 1000 && $1 < 4000 {print substr($1,0,4)}'); do
$IP rule del pref $(($id+1000)) $IP rule del pref $rule_id
done
while [ -n "$($IP rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do
$IP rule del pref $(($id+2000))
done done
} }
@ -713,39 +535,6 @@ mwan3_delete_iface_ipset_entries()
done done
} }
mwan3_rtmon()
{
local protocol
for protocol in "ipv4" "ipv6"; do
pid="$(pgrep -f "mwan3rtmon $protocol")"
[ "$protocol" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
if [ "${pid}" = "" ]; then
[ -x /usr/sbin/mwan3rtmon ] && /usr/sbin/mwan3rtmon $protocol &
fi
done
}
mwan3_track()
{
local track_ips pids
mwan3_list_track_ips()
{
track_ips="$track_ips $1"
}
config_list_foreach "$1" track_ip mwan3_list_track_ips
# 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
kill -KILL $(pgrep -f "mwan3track $1 ") > /dev/null 2>&1
fi
if [ -n "$track_ips" ]; then
[ -x /usr/sbin/mwan3track ] && MWAN3_STARTUP=0 /usr/sbin/mwan3track "$1" "$2" "$3" "$4" $track_ips &
fi
}
mwan3_set_policy() mwan3_set_policy()
{ {
@ -776,7 +565,7 @@ mwan3_set_policy()
IPT="$IPT6" IPT="$IPT6"
IPTR="$IPT6R" IPTR="$IPT6R"
fi fi
current="$($IPT -S)" current="$($IPT -S)"$'\n'
update="*mangle" update="*mangle"
if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then
@ -785,7 +574,7 @@ mwan3_set_policy()
total_weight_v4=$weight total_weight_v4=$weight
lowest_metric_v4=$metric lowest_metric_v4=$metric
elif [ "$metric" -eq "$lowest_metric_v4" ]; then elif [ "$metric" -eq "$lowest_metric_v4" ]; then
total_weight_v4=$(($total_weight_v4+$weight)) total_weight_v4=$((total_weight_v4+weight))
total_weight=$total_weight_v4 total_weight=$total_weight_v4
else else
return return
@ -796,7 +585,7 @@ mwan3_set_policy()
total_weight_v6=$weight total_weight_v6=$weight
lowest_metric_v6=$metric lowest_metric_v6=$metric
elif [ "$metric" -eq "$lowest_metric_v6" ]; then elif [ "$metric" -eq "$lowest_metric_v6" ]; then
total_weight_v6=$(($total_weight_v6+$weight)) total_weight_v6=$((total_weight_v6+weight))
total_weight=$total_weight_v6 total_weight=$total_weight_v6
else else
return return
@ -807,9 +596,9 @@ mwan3_set_policy()
mwan3_push_update -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))
if [ "$probability" -lt 10 ]; then if [ "$probability" -lt 10 ]; then
probability="0.00$probability" probability="0.00$probability"
elif [ $probability -lt 100 ]; then elif [ $probability -lt 100 ]; then
@ -826,7 +615,7 @@ mwan3_set_policy()
--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
echo "$current" | grep -q "^-A mwan3_policy_$policy.*--comment .* [0-9]* [0-9]*" || echo "$current" | grep -q "^-A mwan3_policy_$policy.*--comment .* [0-9]* [0-9]*" ||
mwan3_push_update -I "mwan3_policy_$policy" \ mwan3_push_update -I "mwan3_policy_$policy" \
@ -855,9 +644,9 @@ mwan3_create_policies_iptables()
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
current="$($IPT -S)" current="$($IPT -S)"$'\n'
update="*mangle" update="*mangle"
if [ -n "${current##*-N mwan3_policy_$1*}" ]; then if [ -n "${current##*-N mwan3_policy_$1$'\n'*}" ]; then
mwan3_push_update -N "mwan3_policy_$1" mwan3_push_update -N "mwan3_policy_$1"
fi fi
@ -915,14 +704,14 @@ mwan3_set_sticky_iptables()
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 if [ -z "${current##*-N mwan3_iface_in_$1$'\n'*}" ]; then
mwan3_push_update -I "mwan3_rule_$rule" \ mwan3_push_update -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"
mwan3_push_update -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
fi fi
done done
@ -952,6 +741,18 @@ mwan3_set_user_iptables_rule()
config_get global_logging globals logging 0 config_get global_logging globals logging 0
config_get loglevel globals loglevel notice config_get loglevel globals loglevel notice
[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
[ "$family" = "ipv4" ] && [ "$ipv" = "ipv6" ] && return
[ "$family" = "ipv6" ] && [ "$ipv" = "ipv4" ] && return
for ipaddr in "$src_ip" "$dest_ip"; do
if [ -n "$ipaddr" ] && { { [ "$ipv" = "ipv4" ] && echo "$ipaddr" | grep -qE "$IPv6_REGEX"; } ||
{ [ "$ipv" = "ipv6" ] && echo "$ipaddr" | grep -qE $IPv4_REGEX; } }; then
LOG warn "invalid $ipv address $ipaddr specified for rule $rule"
return
fi
done
if [ -n "$src_iface" ]; then if [ -n "$src_iface" ]; then
network_get_device src_dev "$src_iface" network_get_device src_dev "$src_iface"
if [ -z "$src_dev" ]; then if [ -z "$src_dev" ]; then
@ -1013,16 +814,12 @@ mwan3_set_user_iptables_rule()
fi fi
fi fi
[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy$'\n'*}" ]; then
[ "$family" = "ipv4" ] && [ "$ipv" = "ipv6" ] && return
[ "$family" = "ipv6" ] && [ "$ipv" = "ipv4" ] && return
if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy*}" ]; then
mwan3_push_update -N "$policy" mwan3_push_update -N "$policy"
fi fi
if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then
if [ -n "${current##*-N mwan3_rule_$1*}" ]; then if [ -n "${current##*-N mwan3_rule_$1$'\n'*}" ]; then
mwan3_push_update -N "mwan3_rule_$1" mwan3_push_update -N "mwan3_rule_$1"
fi fi
@ -1117,7 +914,7 @@ mwan3_set_user_rules()
fi fi
[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue
update="*mangle" update="*mangle"
current="$($IPT -S)" current="$($IPT -S)"$'\n'
if [ -n "${current##*-N mwan3_rules*}" ]; then if [ -n "${current##*-N mwan3_rules*}" ]; then
@ -1136,6 +933,83 @@ mwan3_set_user_rules()
} }
mwan3_interface_hotplug_shutdown()
{
local interface status device ifdown
interface="$1"
ifdown="$2"
[ -f $MWAN3TRACK_STATUS_DIR/$interface/STATUS ] && {
status=$(cat $MWAN3TRACK_STATUS_DIR/$interface/STATUS)
}
[ "$status" != "online" ] && [ "$ifdown" != 1 ] && return
if [ "$ifdown" = 1 ]; then
env -i ACTION=ifdown \
INTERFACE=$interface \
DEVICE=$device \
sh /etc/hotplug.d/iface/15-mwan3
else
[ "$status" = "online" ] && {
env -i MWAN3_SHUTDOWN="1" \
ACTION="disconnected" \
INTERFACE="$interface" \
DEVICE="$device" /sbin/hotplug-call iface
}
fi
}
mwan3_interface_shutdown()
{
mwan3_interface_hotplug_shutdown $1
mwan3_track_clean $1
}
mwan3_ifup()
{
local up l3_device status interface true_iface mwan3_startup
interface=$1
mwan3_startup=$2
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
/etc/init.d/mwan3 running || {
echo 'The service mwan3 is global disabled.'
echo 'Please execute "/etc/init.d/mwan3 start" first.'
exit 1
}
config_load mwan3
fi
mwan3_get_true_iface true_iface $interface
status=$(ubus -S call network.interface.$true_iface status)
[ -n "$status" ] && {
json_load "$status"
json_get_vars up l3_device
}
hotplug_startup()
{
env -i MWAN3_STARTUP=$mwan3_startup ACTION=ifup \
INTERFACE=$interface DEVICE=$l3_device \
sh /etc/hotplug.d/iface/15-mwan3
}
if [ "$up" != "1" ] || [ -z "$l3_device" ]; then
return
fi
if [ "${mwan3_startup}" = 1 ]; then
hotplug_startup &
hotplug_pids="$hotplug_pids $!"
else
hotplug_startup
fi
}
mwan3_set_iface_hotplug_state() { mwan3_set_iface_hotplug_state() {
local iface=$1 local iface=$1
local state=$2 local state=$2
@ -1151,7 +1025,7 @@ mwan3_get_iface_hotplug_state() {
mwan3_report_iface_status() mwan3_report_iface_status()
{ {
local device result track_ips tracking IP IPT local device result tracking IP IPT
mwan3_get_iface_id id "$1" mwan3_get_iface_id id "$1"
network_get_device device "$1" network_get_device device "$1"
@ -1170,8 +1044,9 @@ mwan3_report_iface_status()
if [ -z "$id" ] || [ -z "$device" ]; then if [ -z "$id" ] || [ -z "$device" ]; then
result="offline" result="offline"
elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" ] && \ elif [ -n "$($IP rule | awk '$1 == "'$((id+1000)):'"')" ] && \
[ -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" ] && \ [ -n "$($IP rule | awk '$1 == "'$((id+2000)):'"')" ] && \
[ -n "$($IP rule | awk '$1 == "'$((id+3000)):'"')" ] && \
[ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] && \ [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] && \
[ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then
json_init json_init
@ -1183,11 +1058,12 @@ mwan3_report_iface_status()
json_get_vars online uptime json_get_vars online uptime
json_select .. json_select ..
json_select .. json_select ..
online="$(printf '%02dh:%02dm:%02ds\n' $(($online/3600)) $(($online%3600/60)) $(($online%60)))" online="$(printf '%02dh:%02dm:%02ds\n' $((online/3600)) $((online%3600/60)) $((online%60)))"
uptime="$(printf '%02dh:%02dm:%02ds\n' $(($uptime/3600)) $(($uptime%3600/60)) $(($uptime%60)))" uptime="$(printf '%02dh:%02dm:%02ds\n' $((uptime/3600)) $((uptime%3600/60)) $((uptime%60)))"
result="$(mwan3_get_iface_hotplug_state $1) $online, uptime $uptime" result="$(mwan3_get_iface_hotplug_state $1) $online, uptime $uptime"
elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" ] || \ elif [ -n "$($IP rule | awk '$1 == "'$((id+1000)):'"')" ] || \
[ -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" ] || \ [ -n "$($IP rule | awk '$1 == "'$((id+2000)):'"')" ] || \
[ -n "$($IP rule | awk '$1 == "'$((id+3000)):'"')" ] || \
[ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] || \ [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] || \
[ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then
result="error" result="error"
@ -1197,22 +1073,7 @@ mwan3_report_iface_status()
result="disabled" result="disabled"
fi fi
mwan3_list_track_ips() tracking="$(mwan3_get_mwan3track_status $1)"
{
track_ips="$1 $track_ips"
}
config_list_foreach "$1" track_ip mwan3_list_track_ips
if [ -n "$track_ips" ]; then
if [ -n "$(pgrep -f "mwan3track $1 $device")" ]; then
tracking="active"
else
tracking="down"
fi
else
tracking="not enabled"
fi
echo " interface $1 is $result and tracking is $tracking" echo " interface $1 is $result and tracking is $tracking"
} }
@ -1225,10 +1086,10 @@ mwan3_report_policies()
total_weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}') total_weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
if [ ! -z "${total_weight##*[!0-9]*}" ]; then if [ -n "${total_weight##*[!0-9]*}" ]; then
for iface in $($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do for iface in $($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do
weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}') weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
percent=$(($weight*100/$total_weight)) percent=$((weight*100/total_weight))
echo " $iface ($percent%)" echo " $iface ($percent%)"
done done
else else
@ -1306,6 +1167,6 @@ 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
rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR" rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR"
} }

View file

@ -77,16 +77,13 @@ get_mwan3_status() {
local online=0 local online=0
local offline=0 local offline=0
local up="0" local up="0"
local enabled pid device time_p time_n time_u time_d status local enabled device time_p time_n time_u time_d status track_status
network_get_device device $1 network_get_device device $1
if [ "${iface}" = "${iface_select}" ] || [ "${iface_select}" = "" ]; then if [ "${iface}" = "${iface_select}" ] || [ "${iface_select}" = "" ]; then
pid="$(pgrep -f "mwan3track $iface $device")" track_status="$(mwan3_get_mwan3track_status "$1")"
if [ "${pid}" != "" ]; then [ "$track_status" = "active" ] && running="1"
running="1"
fi
time_p="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/TIME")" time_p="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/TIME")"
[ -z "${time_p}" ] || { [ -z "${time_p}" ] || {
time_n="$(get_uptime)" time_n="$(get_uptime)"

View file

@ -22,29 +22,27 @@ Available commands:
connected Show directly connected networks connected Show directly connected networks
rules Show active rules rules Show active rules
status Show all status status Show all status
use <iface> <cmd> Run a command bound to <iface> and avoid mwan3 rules
EOF EOF
} }
ifdown()
{ ifdown() {
if [ -z "$1" ]; then if [ -z "$1" ]; then
echo "Error: Expecting interface. Usage: mwan3 ifdown <interface>" && exit 0 echo "Error: Expecting interface. Usage: mwan3 ifdown <interface>"
exit 0
fi fi
if [ -n "$2" ]; then if [ -n "$2" ]; then
echo "Error: Too many arguments. Usage: mwan3 ifdown <interface>" && exit 0 echo "Error: Too many arguments. Usage: mwan3 ifdown <interface>"
exit 0
fi fi
ACTION=ifdown INTERFACE=$1 /sbin/hotplug-call iface mwan3_interface_hotplug_shutdown "$1" 1
kill $(pgrep -f "mwan3track $1 ") &> /dev/null
mwan3_track_clean $1
} }
ifup() ifup() {
{ . /etc/init.d/mwan3
local device enabled up l3_device status interface true_iface
if [ -z "$1" ]; then if [ -z "$1" ]; then
echo "Expecting interface. Usage: mwan3 ifup <interface>" echo "Expecting interface. Usage: mwan3 ifup <interface>"
@ -56,46 +54,7 @@ ifup()
exit 0 exit 0
fi fi
interface=$1 mwan3_ifup "$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_get_bool enabled globals 'enabled' 0
[ ${enabled} -gt 0 ] || {
echo "The service mwan3 is global disabled."
echo "Please execute \"/etc/init.d/mwan3 start\" first."
exit 1
}
else
enabled=1
fi
mwan3_get_true_iface true_iface $interface
status=$(ubus -S call network.interface.$true_iface status)
[ -n "$status" ] && {
json_load "$status"
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" ] || [ -z "$l3_device" ] || [ "$enabled" != "1" ]; then
return
fi
if [ "${MWAN3_STARTUP}" = 1 ]; then
hotplug_startup &
hotplug_pids="$hotplug_pids $!"
else
hotplug_startup
fi
} }
interfaces() interfaces()
@ -104,40 +63,40 @@ interfaces()
echo "Interface status:" echo "Interface status:"
config_foreach mwan3_report_iface_status interface config_foreach mwan3_report_iface_status interface
echo -e echo
} }
policies() policies()
{ {
echo "Current ipv4 policies:" echo "Current ipv4 policies:"
mwan3_report_policies_v4 mwan3_report_policies_v4
echo -e echo
[ $NO_IPV6 -ne 0 ] && return [ $NO_IPV6 -ne 0 ] && return
echo "Current ipv6 policies:" echo "Current ipv6 policies:"
mwan3_report_policies_v6 mwan3_report_policies_v6
echo -e echo
} }
connected() connected()
{ {
echo "Directly connected ipv4 networks:" echo "Directly connected ipv4 networks:"
mwan3_report_connected_v4 mwan3_report_connected_v4
echo -e echo
[ $NO_IPV6 -ne 0 ] && return [ $NO_IPV6 -ne 0 ] && return
echo "Directly connected ipv6 networks:" echo "Directly connected ipv6 networks:"
mwan3_report_connected_v6 mwan3_report_connected_v6
echo -e echo
} }
rules() rules()
{ {
echo "Active ipv4 user rules:" echo "Active ipv4 user rules:"
mwan3_report_rules_v4 mwan3_report_rules_v4
echo -e echo
[ $NO_IPV6 -ne 0 ] && return [ $NO_IPV6 -ne 0 ] && return
echo "Active ipv6 user rules:" echo "Active ipv6 user rules:"
mwan3_report_rules_v6 mwan3_report_rules_v6
echo -e echo
} }
status() status()
@ -148,113 +107,51 @@ status()
rules rules
} }
start() start() {
{ /etc/init.d/mwan3 enable
local enabled hotplug_pids MWAN3_STARTUP /etc/init.d/mwan3 start
MWAN3_STARTUP=1
mwan3_lock "command" "mwan3"
uci_toggle_state mwan3 globals enabled "1"
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
wait $hotplug_pids
mwan3_add_all_nondefault_routes
mwan3_set_policies_iptables
mwan3_set_user_rules
mwan3_unlock "command" "mwan3"
mwan3_rtmon
unset MWAN3_STARTUP
} }
stop() stop() {
{ /etc/init.d/mwan3 disable
local ipset rule IP IPTR IPT kill_pid family table tid /etc/init.d/mwan3 stop
mwan3_lock "command" "mwan3"
uci_toggle_state mwan3 globals enabled "0"
{
kill -TERM $(pgrep -f "mwan3rtmon") > /dev/null 2>&1
kill -TERM $(pgrep -f "mwan3track") > /dev/null 2>&1
sleep 1
kill -KILL $(pgrep -f "mwan3rtmon") > /dev/null 2>&1
kill -KILL $(pgrep -f "mwan3track") > /dev/null 2>&1
} &
kill_pid=$!
config_load mwan3
config_foreach mwan3_track_clean interface
for family in ipv4 ipv6; do
if [ "$family" = "ipv4" ]; then
IPT="$IPT4"
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
for rule in $($IP rule list | egrep '^[1-2][0-9]{3}\:' | cut -d ':' -f 1); do
$IP rule del pref $rule &> /dev/null
done
table="$($IPT -S)"
{
echo "*mangle";
[ -z "${table##*PREROUTING -j mwan3_hook*}" ] && echo "-D PREROUTING -j mwan3_hook"
[ -z "${table##*OUTPUT -j mwan3_hook*}" ] && echo "-D OUTPUT -j mwan3_hook"
echo "$table" | awk '{print "-F "$2}' | grep mwan3 | sort -u
echo "$table" | awk '{print "-X "$2}' | grep mwan3 | sort -u
echo "COMMIT"
} | $IPTR
done
for ipset in $($IPS -n list | grep mwan3_); do
$IPS -q destroy $ipset
done
for ipset in $($IPS -n list | grep mwan3 | grep -E '_v4|_v6'); do
$IPS -q destroy $ipset
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"
} }
restart() { restart() {
stop /etc/init.d/mwan3 enable
start /etc/init.d/mwan3 stop
/etc/init.d/mwan3 start
}
wrap() {
# Run a command with the device, src_ip and fwmark set to avoid processing by mwan3
# firewall rules
local interface device src_ip family
mwan3_init
config_load mwan3
interface=$1 ; shift
[ -z "$*" ] && echo "no command specified for mwan3 wrap" && return
network_get_device device $interface
[ -z "$device" ] && echo "could not find device for $interface" && return
mwan3_get_src_ip src_ip $interface
[ -z "$src_ip" ] && echo "could not find src_ip for $interface" && return
config_get family $interface family
[ -z "$family" ] && echo "could not find family for $interface. Using ipv4." && family='ipv4'
echo "Running '$*' with DEVICE=$device SRCIP=$src_ip FWMARK=$MMX_DEFAULT FAMILY=$family"
# shellcheck disable=SC2048
FAMILY=$family DEVICE=$device SRCIP=$src_ip FWMARK=$MMX_DEFAULT LD_PRELOAD=/lib/mwan3/libwrap_mwan3_sockopt.so.1.0 $*
} }
case "$1" in case "$1" in
ifup|ifdown|interfaces|policies|connected|rules|status|start|stop|restart) ifup|ifdown|interfaces|policies|connected|rules|status|start|stop|restart|use)
mwan3_init mwan3_init
# shellcheck disable=SC2048
$* $*
;; ;;
*) *)

View file

@ -5,82 +5,139 @@
. /lib/mwan3/mwan3.sh . /lib/mwan3/mwan3.sh
. /lib/mwan3/common.sh . /lib/mwan3/common.sh
trap_with_arg()
{
func="$1" ; shift
pid="$1" ; shift
for sig ; do
# shellcheck disable=SC2064
trap "$func $sig $pid" "$sig"
done
}
func_trap()
{
kill -${1} ${2} 2>/dev/null
}
mwan3_add_all_routes()
{
local tid IP IPT route_line family active_tbls tid initial_state
local ipv=$1
add_active_tbls()
{
let tid++
config_get family "$1" family ipv4
config_get initial_state "$1" initial_state "online"
[ "$family" != "$ipv" ] && return
if $IPT -S "mwan3_iface_in_$1" &> /dev/null; then
active_tbls="$active_tbls${tid} "
fi
}
add_route()
{
let tid++
[ -n "${active_tbls##* $tid *}" ] && return
$IP route add table $tid $route_line ||
LOG warn "failed to add $route_line to table $tid"
}
mwan3_update_dev_to_table
[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
if [ "$ipv" = "ipv4" ]; then
IP="$IP4"
IPT="$IPT4"
elif [ "$ipv" = "ipv6" ]; then
IP="$IP6"
IPT="$IPT6"
fi
tid=0
active_tbls=" "
config_foreach add_active_tbls interface
[ $active_tbls = " " ] && return
mwan3_get_routes | while read -r route_line; do
mwan3_route_line_dev "tid" "$route_line" "$ipv"
if [ -n "$tid" ] && [ -z "${active_tbls##* $tid *}" ]; then
$IP route add table $tid $route_line
elif [ -n "${route_line##default*}" ] && [ -n "${route_line##fe80::/64*}" ]; then
config_foreach add_route interface
fi
done
}
mwan3_rtmon_route_handle() mwan3_rtmon_route_handle()
{ {
config_load mwan3 local action route_line family tbl device line route_line_exp tid source_routing
local section action route_line family tbl device metric tos dst line
local route_device tid
route_line=${1##"Deleted "} route_line=${1##"Deleted "}
route_family=$2 route_family=$2
config_get_boolean source_routing globals source_routing 0
[ $source_routing -eq 0 ] && unset source_routing
if [ "$route_line" = "$1" ]; then
action="replace"
route_line_exp="s/expires \([0-9]\+\)sec//;s/error [0-9]\+//; ${source_routing:+s/default\(.*\) from [^ ]*/default\1/}"
$IPS -! add mwan3_connected_${route_family##ip} ${route_line%% *}
else
action="del"
route_line_exp="s/expires [0-9]\+sec//;s/error [0-9]\+//; ${source_routing:+s/default\(.*\) from [^ ]*/default\1/}"
mwan3_set_connected_${route_family}
fi
if [ "$route_family" = "ipv4" ]; then if [ "$route_family" = "ipv4" ]; then
IP="$IP4" IP="$IP4"
elif [ "$route_family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then elif [ "$route_family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
IP="$IP6" IP="$IP6"
route_line=$(echo "$route_line" | sed "$route_line_exp")
else else
LOG warn "route update called with invalid family - $route_family"
return return
fi fi
if [ "$route_line" == "$1" ]; then # don't try to add routes when link has gone down
action="add" if [ -z "${route_line##linkdown*}" ]; then
else LOG debug "not adding route due to linkdown - skipping $route_line"
action="del" return
fi fi
# never add default route lines, since this is handled elsewhere
[ -z "${route_line##default*}" ] && return
[ -z "${route_line##::/0*}" ] && return
route_line=${route_line%% linkdown*}
route_line=${route_line%% unreachable*}
mwan3_update_dev_to_table
mwan3_route_line_dev "tid" "$route_line" "$route_family"
handle_route() { handle_route() {
tbl=$($IP route list table $tid) local iface=$1
if [ $action = "add" ]; then tbl=$($IP route list table $tid 2>/dev/null)$'\n'
echo "$tbl" | grep -q "^default\|^::/0" || return
else
[ -z "$tbl" ] && return
fi
# check that action needs to be performed. May not need to take action if:
# Got a route update on ipv6 where route is already in the table
# Got a delete event, but table was already flushed
[ $action = "add" ] && [ -z "${tbl##*$route_line*}" ] && return if [ "$(cat /var/run/mwan3track/$iface/STATUS)" != "online" ]; then
[ $action = "del" ] && [ -n "${tbl##*$route_line*}" ] && return LOG debug "interface $iface is offline - skipping $route_line";
network_get_device device "$section" return
LOG debug "adjusting route $device: $IP route "$action" table $tid $route_line" fi
# check that action needs to be performed. May not need to take action if we
# got a delete event, but table was already flushed
if [ $action = "del" ] && [ -n "${tbl##*$route_line$'\n'*}" ]; then
LOG debug "skipping already deleted route table $tid - skipping $route_line"
return
fi
network_get_device device "$iface"
LOG debug "adjusting route $device: $IP route $action table $tid $route_line"
$IP route "$action" table $tid $route_line || $IP route "$action" table $tid $route_line ||
LOG warn "failed: $IP route $action table $tid $route_line" LOG warn "failed: $IP route $action table $tid $route_line"
} }
handle_route_cb(){ handle_route_cb(){
local iface=$1
let tid++ let tid++
config_get family "$section" family ipv4 config_get family "$iface" family ipv4
[ "$family" != "$route_family" ] && return [ "$family" != "$route_family" ] && return
handle_route handle_route "$iface"
} }
if [ $action = "add" ]; then mwan3_update_dev_to_table
## handle old routes from 'change' or 'replace' mwan3_route_line_dev "tid" "$route_line" "$route_family"
metric=${route_line##*metric }
[ "$metric" = "$route_line" ] && unset metric || metric=${metric%% *}
tos=${route_line##*tos }
[ "$tos" = "$route_line" ] && unset tos || tos=${tos%% *}
dst=${route_line%% *}
grep_line="$dst ${tos:+tos $tos}.*table [0-9].*${metric:+metric $metric}"
$IP route list table all | grep "$grep_line" | while read line; do
tbl=${line##*table }
tbl=${tbl%% *}
[ $tbl -gt $MWAN3_INTERFACE_MAX ] && continue
LOG debug "removing route on ip route change/replace: $line"
$IP route del $line
done
fi
if [ -n "$tid" ]; then if [ -n "$tid" ]; then
handle_route handle_route
else elif [ -n "${route_line##default*}" ] && [ -n "${route_line##fe80::/64*}" ]; then
config_foreach handle_route_cb interface config_foreach handle_route_cb interface
fi fi
} }
@ -92,19 +149,35 @@ main()
config_load mwan3 config_load mwan3
family=$1 family=$1
[ -z $family ] && family=ipv4 [ -z $family ] && family=ipv4
if [ "$family" = ipv6 ]; then if [ "$family" = "ipv6" ]; then
if [ $NO_IPV6 -ne 0 ]; then
LOG warn "mwan3rtmon started for ipv6, but ipv6 not enabled on system"
exit 1
fi
IP="$IP6" IP="$IP6"
else else
IP="$IP4" IP="$IP4"
fi fi
mwan3_init mwan3_init
mwan3_lock "mwan3rtmon" "start"
$IP monitor route | while read line; do sh -c "echo \$\$; exec $IP monitor route" | {
read -r monitor_pid
trap_with_arg func_trap "$monitor_pid" SIGINT SIGTERM SIGKILL
while read -r line; do
[ -z "${line##*table*}" ] && continue [ -z "${line##*table*}" ] && continue
LOG debug "handling route update $family $line" LOG debug "handling route update $family $line"
mwan3_lock "service" "mwan3rtmon" mwan3_lock "service" "mwan3rtmon"
mwan3_rtmon_route_handle "$line" "$family" mwan3_rtmon_route_handle "$line" "$family"
mwan3_unlock "service" "mwan3rtmon" mwan3_unlock "service" "mwan3rtmon"
done done
} &
child=$!
kill -SIGSTOP $child
trap_with_arg func_trap "$child" SIGINT SIGTERM SIGKILL
mwan3_set_connected_${family}
mwan3_add_all_routes ${family}
mwan3_unlock "mwan3rtmon" "start"
kill -SIGCONT $child
wait $!
} }
main "$@" main "$@"

View file

@ -1,37 +1,64 @@
#!/bin/sh #!/bin/sh
. /lib/functions.sh . /lib/functions.sh
. /lib/functions/network.sh
. /lib/mwan3/common.sh . /lib/mwan3/common.sh
INTERFACE="" INTERFACE=""
DEVICE="" DEVICE=""
PING="/bin/ping"
IFDOWN_EVENT=0 IFDOWN_EVENT=0
IFUP_EVENT=0 IFUP_EVENT=0
TRACK_OUTPUT=$MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_OUTPUT
mwan3_init
stop_subprocs() {
[ -n "$SLEEP_PID" ] && kill "$SLEEP_PID" && unset SLEEP_PID
[ -n "$TRACK_PID" ] && kill "$TRACK_PID" && unset TRACK_PID
}
WRAP() {
# shellcheck disable=SC2048
FAMILY=$FAMILY DEVICE=$DEVICE SRCIP=$SRC_IP FWMARK=$MMX_DEFAULT LD_PRELOAD=/lib/mwan3/libwrap_mwan3_sockopt.so.1.0 $*
}
clean_up() { clean_up() {
LOG notice "Stopping mwan3track for interface \"${INTERFACE}\"" LOG notice "Stopping mwan3track for interface \"${INTERFACE}\". Status was \"${STATUS}\""
stop_subprocs
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
stop_subprocs
} }
if_up() { if_up() {
LOG info "Detect ifup event on interface ${INTERFACE} (${DEVICE})" LOG info "Detect ifup event on interface ${INTERFACE} (${DEVICE})"
IFDOWN_EVENT=0
IFUP_EVENT=1 IFUP_EVENT=1
STARTED=1
stop_subprocs
} }
validate_track_method() { validate_track_method() {
case "$1" in case "$1" in
ping) ping)
[ -x "$PING" ] || { if [ -x "/usr/bin/ping" ] && [ "$(/usr/bin/ping -V | grep -o '[0-9]*$')" -gt 20150519 ]; then
LOG warn "Missing ping. Please enable ping util and recompile busybox." # -4 option added in iputils c3e68ac6
PING="/usr/bin/ping -${FAMILY#ipv}"
elif [ "$FAMILY" = "ipv6" ] && [ -x "/usr/bin/ping6" ]; then
PING="/usr/bin/ping6"
elif [ "$FAMILY" = "ipv4" ] && [ -x "/usr/bin/ping" ]; then
PING="/usr/bin/ping"
elif [ -x "/bin/ping" ]; then
PING="/bin/ping -${FAMILY#ipv}"
else
LOG warn "Missing ping. Please enable BUSYBOX_DEFAULT_PING and recompile busybox or install iputils-ping package."
return 1 return 1
} fi
;; ;;
arping) arping)
command -v arping 1>/dev/null 2>&1 || { command -v arping 1>/dev/null 2>&1 || {
@ -44,10 +71,6 @@ validate_track_method() {
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" != "::" ] || {
LOG warn "Cannot determine source IP for the interface which is required by httping."
return 1
}
;; ;;
nping-*) nping-*)
command -v nping 1>/dev/null 2>&1 || { command -v nping 1>/dev/null 2>&1 || {
@ -62,30 +85,63 @@ validate_track_method() {
esac esac
} }
validate_wrap() {
[ -x /lib/mwan3/libwrap_mwan3_sockopt.so.1.0 ] && return
LOG error "Missing libwrap_mwan3_sockopt. Please reinstall mwan3." &&
exit 1
}
disconnected() { disconnected() {
echo "offline" > /var/run/mwan3track/$INTERFACE/STATUS STATUS='offline'
echo "$(get_uptime)" > /var/run/mwan3track/$INTERFACE/OFFLINE echo "offline" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
echo "0" > /var/run/mwan3track/$INTERFACE/ONLINE get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE
echo "0" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/ONLINE
score=0 score=0
[ "$1" == 1 ] && return [ "$1" = 1 ] && return
LOG notice "Interface $INTERFACE ($DEVICE) is offline" LOG notice "Interface $INTERFACE ($DEVICE) is offline"
env -i ACTION="disconnected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface env -i ACTION="disconnected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
} }
connected() { connected() {
echo "online" > /var/run/mwan3track/$INTERFACE/STATUS STATUS='online'
echo "0" > /var/run/mwan3track/$INTERFACE/OFFLINE echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
echo "$(get_uptime)" > /var/run/mwan3track/$INTERFACE/ONLINE echo "0" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE
get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/ONLINE
host_up_count=0 host_up_count=0
lost=0 lost=0
turn=0 turn=0
loss=0 loss=0
[ "$1" == 1 ] && return
LOG notice "Interface $INTERFACE ($DEVICE) is online" LOG notice "Interface $INTERFACE ($DEVICE) is online"
env -i ACTION="connected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface env -i FIRSTCONNECT=$1 ACTION="connected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface
}
disabled() {
STATUS='disabled'
echo "disabled" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS
STARTED=0
} }
firstconnect() { firstconnect() {
local true_iface
network_flush_cache
mwan3_get_true_iface true_iface $INTERFACE
network_get_device DEVICE $true_iface
if [ "$STATUS" != "online" ]; then
config_get STATUS $INTERFACE initial_state "online"
fi
if ! network_is_up $true_iface || [ -z "$DEVICE" ]; then
disabled
return
fi
mwan3_get_src_ip SRC_IP $INTERFACE
LOG debug "firstconnect: called on $INTERFACE/$true_iface ($DEVICE). Status is $STATUS. SRC_IP is $SRC_IP"
STARTED=1
if [ "$STATUS" = "offline" ]; then if [ "$STATUS" = "offline" ]; then
disconnected 1 disconnected 1
else else
@ -94,14 +150,12 @@ firstconnect() {
} }
update_status() { update_status() {
local status track_ip local track_ip=$1
track_ip=$1
status=$2
echo "$1" > /var/run/mwan3track/$INTERFACE/TRACK_${track_ip} echo "$2" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
[ -z "$3" ] && return [ -z "$3" ] && return
echo "$3" > /var/run/mwan3track/$INTERFACE/LATENCY_${track_ip} echo "$3" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LATENCY_${track_ip}
echo "$4" > /var/run/mwan3track/$INTERFACE/LOSS_${track_ip} echo "$4" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOSS_${track_ip}
} }
main() { main() {
@ -109,23 +163,23 @@ main() {
local recovery_interval down up size local recovery_interval down up size
local keep_failure_interval check_quality failure_latency local keep_failure_interval check_quality failure_latency
local recovery_latency failure_loss recovery_loss local recovery_latency failure_loss recovery_loss
local max_ttl httping_ssl
[ -z "$5" ] && echo "Error: should not be started manually" && exit 0 local max_ttl httping_ssl track_ips do_log
INTERFACE=$1 INTERFACE=$1
DEVICE=$2 STATUS=""
STATUS=$3 STARTED=0
SRC_IP=$4 mkdir -p $MWAN3TRACK_STATUS_DIR/$INTERFACE
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 trap if_up USR2
config_load mwan3 config_load mwan3
config_get FAMILY $INTERFACE family ipv4
config_get track_method $INTERFACE track_method ping config_get track_method $INTERFACE track_method ping
config_get_bool httping_ssl $INTERFACE httping_ssl 0 config_get_bool httping_ssl $INTERFACE httping_ssl 0
validate_track_method $track_method $SRC_IP || { validate_track_method $track_method || {
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"
@ -150,110 +204,102 @@ main() {
config_get recovery_latency $INTERFACE recovery_latency 500 config_get recovery_latency $INTERFACE recovery_latency 500
config_get failure_loss $INTERFACE failure_loss 40 config_get failure_loss $INTERFACE failure_loss 40
config_get recovery_loss $INTERFACE recovery_loss 10 config_get recovery_loss $INTERFACE recovery_loss 10
local sleep_time result ping_status loss latency
mwan3_list_track_ips()
{
track_ips="$track_ips $1"
}
config_list_foreach "$1" track_ip mwan3_list_track_ips
local score=$(($down+$up)) local score=$((down+up))
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 turn=0 local turn=0
local ping_protocol=4
local sleep_time result ping_result ping_result_raw ping_status loss latency
firstconnect firstconnect
while true; do while true; do
[ $STARTED -eq 0 ] && { sleep $MAX_SLEEP & SLEEP_PID=$!; wait; }
unset SLEEP_PID
sleep_time=$interval sleep_time=$interval
for track_ip in $track_ips; do for track_ip in $track_ips; do
if [ $host_up_count -lt $reliability ]; then if [ $host_up_count -lt $reliability ]; then
case "$track_method" in case "$track_method" in
ping) ping)
# pinging IPv6 hosts with an interface is troublesome
# https://bugs.openwrt.org/index.php?do=details&task_id=2897
# so get the IP address of the interface and use that instead
if [ -z ${track_ip##*:*} ]; then
ping_protocol=6
else
unset SRC_IP
fi
if [ $check_quality -eq 0 ]; then if [ $check_quality -eq 0 ]; then
$PING -$ping_protocol -I ${SRC_IP:-$DEVICE} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null WRAP $PING -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null &
TRACK_PID=$!
wait $TRACK_PID
result=$? result=$?
else else
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)" WRAP $PING -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null > $TRACK_OUTPUT &
TRACK_PID=$!
wait $TRACK_PID
ping_status=$? ping_status=$?
ping_result=$(echo "$ping_result_raw" | tail -n2) loss="$(sed $TRACK_OUTPUT -ne 's/.*\([0-9]\+\)% packet loss.*/\1/p')"
loss="$(echo "$ping_result" | grep "packet loss" | cut -d "," -f3 | awk '{print $1}' | sed -e 's/%//')"
if [ "$ping_status" -ne 0 ] || [ "$loss" -eq 100 ]; then if [ "$ping_status" -ne 0 ] || [ "$loss" -eq 100 ]; then
latency=999999 latency=999999
loss=100 loss=100
else else
latency="$(echo "$ping_result" | grep -E 'rtt|round-trip' | cut -d "=" -f2 | cut -d "/" -f2 | cut -d "." -f1)" latency="$(sed $TRACK_OUTPUT -ne 's%\(rtt\|round-trip\).* = [^/]*/\([0-9]\+\).*%\2%p')"
fi fi
fi fi
;; ;;
arping) arping)
arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null WRAP arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null &
TRACK_PID=$!
wait $TRACK_PID
result=$? result=$?
;; ;;
httping) httping)
if [ "$httping_ssl" -eq 1 ]; then if [ "$httping_ssl" -eq 1 ]; then
httping -y $SRC_IP -c $count -t $timeout -q "https://$track_ip" &> /dev/null WRAP httping -c $count -t $timeout -q "https://$track_ip" &> /dev/null &
else else
httping -y $SRC_IP -c $count -t $timeout -q "http://$track_ip" &> /dev/null WRAP httping -c $count -t $timeout -q "http://$track_ip" &> /dev/null &
fi fi
TRACK_PID=$!
wait $TRACK_PID
result=$? result=$?
;; ;;
nping-tcp) nping-*)
result=$(nping -e $DEVICE -c $count $track_ip --tcp | grep Lost | awk '{print $12}') WRAP nping -c $count $track_ip --${FAMILY#nping-} > $TRACK_OUTPUT &
;; TRACK_PID=$!
nping-udp) wait $TRACK_PID
result=$(nping -e $DEVICE -c $count $track_ip --udp | grep Lost | awk '{print $12}') result=$(grep $TRACK_OUTPUT Lost | awk '{print $12}')
;;
nping-icmp)
result=$(nping -e $DEVICE -c $count $track_ip --icmp | grep Lost | awk '{print $12}')
;;
nping-arp)
result=$(nping -e $DEVICE -c $count $track_ip --arp | grep Lost | awk '{print $12}')
;; ;;
esac esac
do_log=""
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++
update_status "$track_ip" "up" update_status "$track_ip" "up"
if [ $score -le $up ]; then [ $score -le $up ] && do_log="success"
LOG info "Check ($track_method) success for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi
else else
let lost++ let lost++
update_status "$track_ip" "down" update_status "$track_ip" "down"
if [ $score -gt $up ]; then [ $score -gt $up ] && do_log="failed"
LOG info "Check ($track_method) failed for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi
fi fi
[ -n "$do_log" ] && LOG info "Check ($track_method) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
else else
if [ "$loss" -ge "$failure_loss" -o "$latency" -ge "$failure_latency" ]; then if [ "$loss" -ge "$failure_loss" ] || [ "$latency" -ge "$failure_latency" ]; then
let lost++ let lost++
update_status "$track_ip" "down" $latency $loss update_status "$track_ip" "down" $latency $loss
if [ $score -gt $up ]; then [ $score -gt $up ] && do_log="failed"
LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) failed for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score" elif [ "$loss" -le "$recovery_loss" ] && [ "$latency" -le "$recovery_latency" ]; then
fi
elif [ "$loss" -le "$recovery_loss" -a "$latency" -le "$recovery_latency" ]; then
let host_up_count++ let host_up_count++
update_status "$track_ip" "up" $latency $loss update_status "$track_ip" "up" $latency $loss
if [ $score -le $up ]; then [ $score -le $up ] && do_log="success"
LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) success for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score" else
echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
fi
[ -n "$do_log" ] && LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score"
fi fi
else else
echo "skipped" > /var/run/mwan3track/$INTERFACE/TRACK_${track_ip} echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip}
fi
fi
else
echo "skipped" > /var/run/mwan3track/$INTERFACE/TRACK_${track_ip}
fi fi
done done
@ -262,9 +308,7 @@ main() {
if [ $score -lt $up ]; then if [ $score -lt $up ]; then
score=0 score=0
[ ${keep_failure_interval} -eq 1 ] && { [ ${keep_failure_interval} -eq 1 ] && sleep_time=$failure_interval
sleep_time=$failure_interval
}
else else
sleep_time=$failure_interval sleep_time=$failure_interval
fi fi
@ -274,31 +318,31 @@ main() {
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 $INTERFACE ($DEVICE). Current score: $score" 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/$INTERFACE/STATUS echo "online" > $MWAN3TRACK_STATUS_DIR/$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
connected $INTERFACE $DEVICE connected
fi fi
fi fi
let turn++ let turn++
mkdir -p "/var/run/mwan3track/${1}" mkdir -p "$MWAN3TRACK_STATUS_DIR/${1}"
echo "${lost}" > /var/run/mwan3track/$INTERFACE/LOST echo "${lost}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOST
echo "${score}" > /var/run/mwan3track/$INTERFACE/SCORE echo "${score}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/SCORE
echo "${turn}" > /var/run/mwan3track/$INTERFACE/TURN echo "${turn}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TURN
echo "$(get_uptime)" > /var/run/mwan3track/$INTERFACE/TIME get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TIME
host_up_count=0 host_up_count=0
sleep "${sleep_time}" & sleep "${sleep_time}" &
@ -306,7 +350,8 @@ main() {
if [ "${IFDOWN_EVENT}" -eq 1 ]; then if [ "${IFDOWN_EVENT}" -eq 1 ]; then
LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})" LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})"
disconnected 1 disabled
disconnected
IFDOWN_EVENT=0 IFDOWN_EVENT=0
fi fi
if [ "${IFUP_EVENT}" -eq 1 ]; then if [ "${IFUP_EVENT}" -eq 1 ]; then

View file

@ -0,0 +1,255 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Aaron Goodman <aaronjg@alumni.stanford.edu>. All Rights Reserved.
*/
/*
* sockopt_wrap.c provides a shared library that intercepts syscalls to various
* networking functions to bind the sockets a source IP address and network device
* and to set the firewall mark on otugoing packets. Parameters are set using the
* DEVICE, SRCIP, FWMARK environment variables.
*
* Additionally the FAMILY environment variable can be set to either 'ipv4' or
* 'ipv6' to cause sockets opened with ipv6 or ipv4 to fail, respectively.
*
* Each environment variable is optional, and if not set, the library will not
* enforce the particular parameter.
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <net/if.h>
static int (*next_socket)(int domain, int type, int protocol);
static int (*next_setsockopt)(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
static int (*next_bind)(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
static int (*next_close)(int fd);
static ssize_t (*next_send)(int sockfd, const void *buf, size_t len, int flags);
static ssize_t (*next_sendto)(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
static ssize_t (*next_sendmsg)(int sockfd, const struct msghdr *msg, int flags);
static int (*next_connect)(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
static int device=0;
static struct sockaddr_in source4 = {0};
#ifdef CONFIG_IPV6
static struct sockaddr_in6 source6 = {0};
#endif
static struct sockaddr * source = 0;
static int sockaddr_size = 0;
static int is_bound [1024] = {0};
#define next_func(x)\
void set_next_##x(){\
if (next_##x) return;\
next_##x = dlsym(RTLD_NEXT, #x);\
dlerror_handle();\
return;\
}
void dlerror_handle()
{
char *msg;
if ((msg = dlerror()) != NULL) {
fprintf(stderr, "socket: dlopen failed : %s\n", msg);
fflush(stderr);
exit(EXIT_FAILURE);
}
}
next_func(bind);
next_func(close);
next_func(setsockopt);
next_func(socket);
next_func(send);
next_func(sendto);
next_func(sendmsg);
next_func(connect);
void dobind(int sockfd)
{
if (source && sockfd < 1024 && !is_bound[sockfd]) {
set_next_bind();
if (next_bind(sockfd, source, sockaddr_size)) {
perror("failed to bind to ip address");
next_close(sockfd);
exit(EXIT_FAILURE);
}
is_bound[sockfd] = 1;
}
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
set_next_connect();
dobind(sockfd);
return next_connect(sockfd, addr, addrlen);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
set_next_send();
dobind(sockfd);
return next_send(sockfd, buf, len, flags);
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen)
{
set_next_sendto();
dobind(sockfd);
return next_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
}
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
set_next_sendmsg();
dobind(sockfd);
return next_sendmsg(sockfd, msg, flags);
}
int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
set_next_bind();
if (device && addr->sa_family == AF_PACKET) {
((struct sockaddr_ll*)addr)->sll_ifindex=device;
}
else if (source && addr->sa_family == AF_INET) {
((struct sockaddr_in*)addr)->sin_addr = source4.sin_addr;
}
#ifdef CONFIG_IPV6
else if (source && addr->sa_family == AF_INET6) {
((struct sockaddr_in6*)addr)->sin6_addr = source6.sin6_addr;
}
#endif
if (sockfd < 1024)
is_bound[sockfd] = 1;
return next_bind(sockfd, addr, addrlen);
}
int close (int sockfd)
{
set_next_close();
if (sockfd < 1024)
is_bound[sockfd]=0;
return next_close(sockfd);
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
{
set_next_setsockopt();
if (level == SOL_SOCKET && (optname == SO_MARK || optname == SO_BINDTODEVICE))
return 0;
return next_setsockopt(sockfd, level, optname, optval, optlen);
}
int socket(int domain, int type, int protocol)
{
int handle;
const char *socket_str = getenv("DEVICE");
const char *srcip_str = getenv("SRCIP");
const char *fwmark_str = getenv("FWMARK");
const char *family_str = getenv("FAMILY");
const int iface_len = socket_str ? strnlen(socket_str, IFNAMSIZ) : 0;
int has_family = family_str && *family_str != 0;
int has_srcip = srcip_str && *srcip_str != 0;
const int fwmark = fwmark_str ? (int)strtol(fwmark_str, NULL, 0) : 0;
set_next_close();
set_next_socket();
set_next_send();
set_next_setsockopt();
set_next_sendmsg();
set_next_sendto();
set_next_connect();
if(has_family) {
#ifdef CONFIG_IPV6
if(domain == AF_INET && strncmp(family_str,"ipv6",4) == 0)
return -1;
#endif
if(domain == AF_INET6 && strncmp(family_str,"ipv4",4) == 0)
return -1;
}
if (domain != AF_INET
#ifdef CONFIG_IPV6
&& domain != AF_INET6
#endif
) {
return next_socket(domain, type, protocol);
}
if (iface_len > 0) {
if (iface_len == IFNAMSIZ) {
fprintf(stderr,"socket: Too long iface name\n");
fflush(stderr);
exit(EXIT_FAILURE);
}
}
if (has_srcip) {
int s;
void * addr_buf;
if (domain == AF_INET) {
addr_buf = &source4.sin_addr;
sockaddr_size=sizeof source4;
memset(&source4, 0, sockaddr_size);
source4.sin_family = domain;
source = (struct sockaddr*)&source4;
}
#ifdef CONFIG_IPV6
else {
addr_buf = &source6.sin6_addr;
sockaddr_size=sizeof source6;
memset(&source6, 0, sockaddr_size);
source6.sin6_family=domain;
source = (struct sockaddr*)&source6;
}
#endif
s = inet_pton(domain, srcip_str, addr_buf);
if (s == 0) {
fprintf(stderr, "socket: ip address invalid format for family %s\n",
domain == AF_INET ? "AF_INET" : domain == AF_INET6 ?
"AF_INET6" : "unknown");
return -1;
}
if (s < 0) {
perror("inet_pton");
exit(EXIT_FAILURE);
}
}
handle = next_socket(domain, type, protocol);
if (handle == -1 ) {
return handle;
}
if (iface_len > 0) {
device=if_nametoindex(socket_str);
if (next_setsockopt(handle, SOL_SOCKET, SO_BINDTODEVICE,
socket_str, iface_len + 1)) {
perror("socket: setting interface name failed with error");
next_close(handle);
exit(EXIT_FAILURE);
}
}
if (fwmark > 0) {
if (next_setsockopt(handle, SOL_SOCKET, SO_MARK,
&fwmark, sizeof fwmark)) {
perror("failed setting mark for socket");
next_close(handle);
exit(EXIT_FAILURE);
}
}
return handle;
}