diff --git a/mwan3/Makefile b/mwan3/Makefile index 6b5e8ce90..958c21061 100644 --- a/mwan3/Makefile +++ b/mwan3/Makefile @@ -8,12 +8,10 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mwan3 -PKG_VERSION:=2.10.13 +PKG_VERSION:=2.8.12 PKG_RELEASE:=1 -PKG_MAINTAINER:=Florian Eckert , \ - Aaron Goodman +PKG_MAINTAINER:=Florian Eckert PKG_LICENSE:=GPL-2.0 -PKG_CONFIG_DEPENDS:=CONFIG_IPV6 include $(INCLUDE_DIR)/package.mk @@ -44,9 +42,12 @@ define Package/mwan3/conffiles /etc/mwan3.user endef +define Build/Compile +endef + define Package/mwan3/postinst #!/bin/sh -if [ -z "$${IPKG_INSTROOT}" ] && [ -x /etc/init.d/rpcd ]; then +if [ -z "$${IPKG_INSTROOT}" ]; then /etc/init.d/rpcd restart fi exit 0 @@ -54,63 +55,14 @@ endef define Package/mwan3/postrm #!/bin/sh -if [ -z "$${IPKG_INSTROOT}" ] && [ -x /etc/init.d/rpcd ]; then +if [ -z "$${IPKG_INSTROOT}" ]; then /etc/init.d/rpcd restart fi exit 0 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 - $(INSTALL_DIR) $(1)/etc/config - $(INSTALL_CONF) ./files/etc/config/mwan3 \ - $(1)/etc/config/ - - $(INSTALL_DIR) $(1)/etc/hotplug.d/iface - $(INSTALL_DATA) ./files/etc/hotplug.d/iface/15-mwan3 \ - $(1)/etc/hotplug.d/iface/ - $(INSTALL_DATA) ./files/etc/hotplug.d/iface/16-mwan3-user \ - $(1)/etc/hotplug.d/iface/ - - $(INSTALL_DIR) $(1)/etc/init.d - $(INSTALL_BIN) ./files/etc/init.d/mwan3 \ - $(1)/etc/init.d/ - - $(INSTALL_DIR) $(1)/lib/mwan3 - $(INSTALL_DATA) ./files/lib/mwan3/common.sh \ - $(1)/lib/mwan3/ - $(INSTALL_DATA) ./files/lib/mwan3/mwan3.sh \ - $(1)/lib/mwan3/ - - $(INSTALL_DIR) $(1)/usr/libexec/rpcd - $(INSTALL_BIN) ./files/usr/libexec/rpcd/mwan3 \ - $(1)/usr/libexec/rpcd/ - - $(INSTALL_DIR) $(1)/usr/sbin - $(INSTALL_BIN) ./files/usr/sbin/mwan3 \ - $(1)/usr/sbin/ - $(INSTALL_BIN) ./files/usr/sbin/mwan3rtmon \ - $(1)/usr/sbin/ - $(INSTALL_BIN) ./files/usr/sbin/mwan3track \ - $(1)/usr/sbin/ - - $(INSTALL_DIR) $(1)/etc - $(INSTALL_BIN) ./files/etc/mwan3.user \ - $(1)/etc/ - - $(CP) $(PKG_BUILD_DIR)/libwrap_mwan3_sockopt.so.1.0 $(1)/lib/mwan3/ - - $(INSTALL_DIR) $(1)/etc/uci-defaults - $(INSTALL_DATA) ./files/etc/uci-defaults/mwan3-migrate-flush_conntrack \ - $(1)/etc/uci-defaults/ +$(CP) ./files/* $(1) endef $(eval $(call BuildPackage,mwan3)) diff --git a/mwan3/files/etc/hotplug.d/iface/15-mwan3 b/mwan3/files/etc/hotplug.d/iface/15-mwan3 index 7c107fc01..645cdd3e4 100644 --- a/mwan3/files/etc/hotplug.d/iface/15-mwan3 +++ b/mwan3/files/etc/hotplug.d/iface/15-mwan3 @@ -3,88 +3,96 @@ . /lib/functions.sh . /lib/functions/network.sh . /lib/mwan3/mwan3.sh -. /lib/mwan3/common.sh +. /usr/share/libubox/jshn.sh -initscript=/etc/init.d/mwan3 -. /lib/functions/procd.sh - - -SCRIPTNAME="mwan3-hotplug" -[ "$ACTION" = "ifup" ] || [ "$ACTION" = "ifdown" ] || [ "$ACTION" = "connected" ] || [ "$ACTION" = "disconnected" ] || exit 1 +[ "$ACTION" == "ifup" -o "$ACTION" == "ifdown" ] || exit 1 [ -n "$INTERFACE" ] || exit 2 -[ "$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" - exit 3 +if [ "$ACTION" == "ifup" ]; then + [ -n "$DEVICE" ] || exit 3 fi -[ "$MWAN3_STARTUP" = "init" ] || procd_lock +mwan3_lock "$ACTION" "$INTERFACE" +config_load mwan3 +config_get_bool enabled globals 'enabled' '0' +[ "${enabled}" -gt 0 ] || { + mwan3_unlock "$ACTION" "$INTERFACE" + exit 0 +} mwan3_init - -/etc/init.d/mwan3 running || { - [ "$MWAN3_STARTUP" = "init" ] || procd_lock - LOG notice "mwan3 hotplug $ACTION on $INTERFACE not called because globally disabled" - exit 0 -} - -$IPT4 -S mwan3_hook &>/dev/null || { - LOG warn "hotplug called on $INTERFACE before mwan3 has been set up" - exit 0 -} - -if [ "$MWAN3_STARTUP" != "init" ] && [ "$ACTION" = "ifup" ]; then - mwan3_set_user_iface_rules $INTERFACE $DEVICE -fi - -config_get_bool enabled $INTERFACE 'enabled' '0' -[ "${enabled}" -eq 1 ] || { - LOG notice "mwan3 hotplug on $INTERFACE not called because interface disabled" - exit 0 -} +mwan3_set_connected_iptables +mwan3_set_custom_ipset config_get initial_state $INTERFACE initial_state "online" -if [ "$initial_state" = "offline" ]; then - status=$(cat $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS 2>/dev/null || echo unknown) - [ "$status" = "online" ] || status=offline -else - status=online +config_get_bool enabled $INTERFACE 'enabled' '0' +[ "${enabled}" -eq 1 ] || { + mwan3_unlock "$ACTION" "$INTERFACE" + exit 0 +} + +if [ "$ACTION" = "ifup" ]; then + 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 -LOG notice "Execute $ACTION event on interface $INTERFACE (${DEVICE:-unknown})" +if [ "$initial_state" = "offline" ]; then + json_load "$(ubus call mwan3 status '{"section":"interfaces"}')" + json_select "interfaces" + json_select "${INTERFACE}" + json_get_var running running + json_get_var status status +else + status=online + running=1 +fi + +$LOG notice "Execute "$ACTION" event on interface $INTERFACE (${DEVICE:-unknown})" case "$ACTION" in - connected) - mwan3_set_iface_hotplug_state $INTERFACE "online" - mwan3_set_policies_iptables - ;; ifup) + mwan3_set_general_rules + mwan3_set_general_iptables mwan3_create_iface_iptables $INTERFACE $DEVICE mwan3_create_iface_rules $INTERFACE $DEVICE - mwan3_set_iface_hotplug_state $INTERFACE "$status" - if [ "$MWAN3_STARTUP" != "init" ]; then - mwan3_create_iface_route $INTERFACE $DEVICE - mwan3_set_general_rules - [ "$status" = "online" ] && mwan3_set_policies_iptables + mwan3_create_iface_route $INTERFACE $DEVICE + if [ "${running}" -eq 1 ] && [ "${status}" = "online" ]; then + $LOG notice "Starting tracker on interface $INTERFACE (${DEVICE:-unknown})" + mwan3_set_iface_hotplug_state $INTERFACE "online" + mwan3_track $INTERFACE $DEVICE "online" "$src_ip" + else + $LOG notice "Starting tracker on interface $INTERFACE (${DEVICE:-unknown})" + mwan3_set_iface_hotplug_state $INTERFACE "offline" + mwan3_track $INTERFACE $DEVICE "offline" "$src_ip" fi - [ "$ACTION" = ifup ] && procd_running mwan3 "track_$INTERFACE" && procd_send_signal mwan3 "track_$INTERFACE" USR2 - ;; - disconnected) - mwan3_set_iface_hotplug_state $INTERFACE "offline" mwan3_set_policies_iptables - ;; + mwan3_set_user_rules + ;; ifdown) mwan3_set_iface_hotplug_state $INTERFACE "offline" mwan3_delete_iface_ipset_entries $INTERFACE - mwan3_delete_iface_rules $INTERFACE - mwan3_delete_iface_route $INTERFACE - mwan3_delete_iface_iptables $INTERFACE - procd_running mwan3 "track_$INTERFACE" && procd_send_signal mwan3 "track_$INTERFACE" USR1 + mwan3_track_signal $INTERFACE $DEVICE mwan3_set_policies_iptables + mwan3_set_user_rules ;; esac -mwan3_flush_conntrack "$INTERFACE" "$ACTION" +mwan3_unlock "$ACTION" "$INTERFACE" exit 0 diff --git a/mwan3/files/etc/hotplug.d/iface/16-mwan3 b/mwan3/files/etc/hotplug.d/iface/16-mwan3 new file mode 100644 index 000000000..dd09358eb --- /dev/null +++ b/mwan3/files/etc/hotplug.d/iface/16-mwan3 @@ -0,0 +1,27 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/network.sh +. /lib/mwan3/mwan3.sh + +mwan3_lock "$ACTION" "mwan3rtmon" + +config_load mwan3 +config_get_bool enabled globals 'enabled' '0' +[ "${enabled}" -gt 0 ] || { + mwan3_unlock "$ACTION" "mwan3rtmon" + exit 0 +} + +if [ "$ACTION" = "ifup" ]; then + mwan3_rtmon +fi + +config_get_bool enabled "$INTERFACE" 'enabled' '0' +[ "${enabled}" -eq 0 ] || { + mwan3_flush_conntrack "$INTERFACE" "$ACTION" +} + +mwan3_unlock "$ACTION" "mwan3rtmon" + +exit 0 diff --git a/mwan3/files/etc/hotplug.d/iface/16-mwan3-user b/mwan3/files/etc/hotplug.d/iface/16-mwan3-user index a9f42168b..af28b1f4f 100644 --- a/mwan3/files/etc/hotplug.d/iface/16-mwan3-user +++ b/mwan3/files/etc/hotplug.d/iface/16-mwan3-user @@ -3,23 +3,26 @@ [ -f "/etc/mwan3.user" ] && { . /lib/functions.sh . /lib/mwan3/mwan3.sh - initscript=/etc/init.d/mwan3 - . /lib/functions/procd.sh - [ "$MWAN3_SHUTDOWN" != 1 ] && procd_lock - - [ "$MWAN3_SHUTDOWN" != 1 ] && ! /etc/init.d/mwan3 running && { - exit 0 - } + mwan3_lock "$ACTION" "user" config_load mwan3 + config_get_bool enabled globals 'enabled' '0' + [ "${enabled}" -gt 0 ] || { + mwan3_unlock "$ACTION" "user" + exit 0 + } config_get_bool enabled "$INTERFACE" enabled 0 [ "${enabled}" -eq 1 ] || { + mwan3_unlock "$ACTION" "user" exit 0 } - env -i ACTION="$ACTION" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /etc/mwan3.user + mwan3_unlock "$ACTION" "user" + + env -i ACTION="$ACTION" INTERFACE="$INTERFACE" DEVICE="$DEVICE" \ + /bin/sh /etc/mwan3.user } exit 0 diff --git a/mwan3/files/etc/init.d/mwan3 b/mwan3/files/etc/init.d/mwan3 index 560621516..e0c65889a 100755 --- a/mwan3/files/etc/init.d/mwan3 +++ b/mwan3/files/etc/init.d/mwan3 @@ -1,111 +1,31 @@ #!/bin/sh /etc/rc.common -. "${IPKG_INSTROOT}/lib/mwan3/common.sh" -. "${IPKG_INSTROOT}/lib/functions/network.sh" -. "${IPKG_INSTROOT}/lib/mwan3/mwan3.sh" - START=19 USE_PROCD=1 -SCRIPTNAME="mwan3-init" -service_running() { - [ -d "$MWAN3_STATUS_DIR" ] +boot() { + . /lib/config/uci.sh + uci_toggle_state mwan3 globals enabled "1" + mwan3_boot=1 + rc_procd start_service } -start_tracker() { - local enabled interface - interface=$1 - config_get_bool enabled $interface 'enabled' '0' - [ $enabled -eq 0 ] && return - [ -z "$(config_get $interface track_ip)" ] && return +# FIXME +# fd 1000 is an inherited lock file descriptor for preventing concurrent +# init script executions. Close it here to prevent the mwan3 daemon from +# inheriting it further to avoid holding the lock indefinitely. - procd_open_instance "track_${1}" - procd_set_param command /usr/sbin/mwan3track $interface - procd_set_param respawn - procd_close_instance +reload_service() { + /usr/sbin/mwan3 restart 1000>&- } start_service() { - local enabled hotplug_pids - - mwan3_init - config_foreach start_tracker interface - - 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 "init" - wait $hotplug_pids - mwan3_set_policies_iptables - mwan3_set_user_rules - - 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 + [ -n "${mwan3_boot}" ] && return 0 + /usr/sbin/mwan3 start 1000>&- } stop_service() { - local ipset rule IP IPTR IPT family table tid - - 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 - -} - -reload_service() { - stop - start + /usr/sbin/mwan3 stop 1000>&- } service_triggers() { diff --git a/mwan3/files/etc/mwan3.user b/mwan3/files/etc/mwan3.user index 2e8e51748..39989ab9b 100644 --- a/mwan3/files/etc/mwan3.user +++ b/mwan3/files/etc/mwan3.user @@ -8,13 +8,9 @@ # There are three main environment variables that are passed to this script. # # $ACTION -# Is called by netifd and mwan3track. -# Is called by netifd and mwan3track. -# Is only called by mwan3track if tracking was successful. -# Is only called by mwan3track if tracking has failed. -# $INTERFACE Name of the interface an action relates to (e.g. "wan" or "wwan"). -# $DEVICE Physical device name of the interface the action relates to (e.g. "eth0" or "wwan0"). -# Note: On an ifdown event, $DEVICE is not available, use $INTERFACE instead. -# -# Further documentation can be found here: -# https://openwrt.org/docs/guide-user/network/wan/multiwan/mwan3#alertsnotifications +# Is called by netifd and mwan3track +# Is called by netifd and mwan3track +# Is only called by mwan3track if tracking was successful +# Is only called by mwan3track if tracking has failed +# $INTERFACE Name of the interface which went up or down (e.g. "wan" or "wwan") +# $DEVICE Physical device name which interface went up or down (e.g. "eth0" or "wwan0") diff --git a/mwan3/files/lib/mwan3/common.sh b/mwan3/files/lib/mwan3/common.sh index 33a94ae86..1af129919 100644 --- a/mwan3/files/lib/mwan3/common.sh +++ b/mwan3/files/lib/mwan3/common.sh @@ -1,197 +1,6 @@ #!/bin/sh -IP4="ip -4" -IP6="ip -6" -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)) - -command -v ip6tables > /dev/null -NO_IPV6=$? - -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 "$*" -} - -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 mmdefault source_routing - - config_load mwan3 - - [ -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_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<>bit_msk)&1)) = "1" ]; then - if [ $((($1>>bit_val)&1)) = "1" ]; then - result=$((result|(1</dev/null)" - [ -z "${time_u}" ] || [ "${time_u}" = "0" ] || { - time_n="$(get_uptime)" - echo $((time_n-time_u)) - } -} diff --git a/mwan3/files/lib/mwan3/mwan3.sh b/mwan3/files/lib/mwan3/mwan3.sh index a3a5ac89b..3c7422dc0 100644 --- a/mwan3/files/lib/mwan3/mwan3.sh +++ b/mwan3/files/lib/mwan3/mwan3.sh @@ -1,12 +1,13 @@ #!/bin/sh -. "${IPKG_INSTROOT}/usr/share/libubox/jshn.sh" +. /usr/share/libubox/jshn.sh +IP4="ip -4" +IP6="ip -6" IPS="ipset" IPT4="iptables -t mangle -w" IPT6="ip6tables -t mangle -w" -IPT4R="iptables-restore -T mangle -w -n" -IPT6R="ip6tables-restore -T mangle -w -n" +LOG="logger -t mwan3[$$] -p" CONNTRACK_FILE="/proc/net/nf_conntrack" 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}:|" @@ -20,73 +21,105 @@ 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}::(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])" -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 +MMX_MASK="" +MMX_DEFAULT="" +MMX_BLACKHOLE="" +MM_BLACKHOLE="" -mwan3_push_update() +MMX_UNREACHABLE="" +MM_UNREACHABLE="" + +command -v ip6tables > /dev/null +NO_IPV6=$? + +# return true(=0) if has any mwan3 interface enabled +# otherwise return false +mwan3_rtmon_ipv4() { - # helper function to build an update string to pass on to - # IPTR or IPS RESTORE. Modifies the 'update' variable in - # the local scope. - update="$update"$'\n'"$*"; -} + local idx=0 + local ret=1 + local tbl="" -mwan3_update_dev_to_table() -{ - local _tid - # shellcheck disable=SC2034 - mwan3_dev_tbl_ipv4=" " - # shellcheck disable=SC2034 - mwan3_dev_tbl_ipv6=" " + local tid family enabled - update_table() - { - local family curr_table device enabled - let _tid++ - config_get family "$1" family ipv4 - network_get_device device "$1" - [ -z "$device" ] && return - config_get enabled "$1" enabled - [ "$enabled" -eq 0 ] && return - curr_table=$(eval "echo \"\$mwan3_dev_tbl_${family}\"") - export "mwan3_dev_tbl_$family=${curr_table}${device}=$_tid " - } - network_flush_cache - config_foreach update_table interface -} + mkdir -p /tmp/mwan3rtmon + ($IP4 route list table main | grep -v "^default\|linkdown" | sort -n; echo empty fixup) >/tmp/mwan3rtmon/ipv4.main + while uci get mwan3.@interface[$idx] >/dev/null 2>&1 ; do + tid=$((idx+1)) -mwan3_update_iface_to_table() -{ - local _tid - mwan3_iface_tbl=" " - update_table() - { - let _tid++ - export mwan3_iface_tbl="${mwan3_iface_tbl}${1}=$_tid " - } - config_foreach update_table interface -} + family="$(uci -q get mwan3.@interface[$idx].family)" + [ -z "$family" ] && family="ipv4" -mwan3_route_line_dev() -{ - # must have mwan3 config already loaded - # arg 1 is route device - local _tid route_line route_device route_family entry curr_table - route_line=$2 - route_family=$3 - route_device=$(echo "$route_line" | sed -ne "s/.*dev \([^ ]*\).*/\1/p") - unset "$1" - [ -z "$route_device" ] && return + enabled="$(uci -q get mwan3.@interface[$idx].enabled)" + [ -z "$enabled" ] && enabled="0" - curr_table=$(eval "echo \"\$mwan3_dev_tbl_${route_family}\"") - for entry in $curr_table; do - if [ "${entry%%=*}" = "$route_device" ]; then - _tid=${entry##*=} - export "$1=$_tid" - return + [ "$family" = "ipv4" ] && { + tbl=$($IP4 route list table $tid 2>/dev/null) + if echo "$tbl" | grep -q ^default; then + (echo "$tbl" | grep -v "^default\|linkdown" | sort -n; echo empty fixup) >/tmp/mwan3rtmon/ipv4.$tid + cat /tmp/mwan3rtmon/ipv4.$tid | grep -v -x -F -f /tmp/mwan3rtmon/ipv4.main | while read line; do + $IP4 route del table $tid $line + done + cat /tmp/mwan3rtmon/ipv4.main | grep -v -x -F -f /tmp/mwan3rtmon/ipv4.$tid | while read line; do + $IP4 route add table $tid $line + done + fi + } + if [ "$enabled" = "1" ]; then + ret=0 fi + idx=$((idx+1)) done + rm -f /tmp/mwan3rtmon/ipv4.* + return $ret +} + +# return true(=0) if has any mwan3 interface enabled +# otherwise return false +mwan3_rtmon_ipv6() +{ + local idx=0 + local ret=1 + local tbl="" + + local tid family enabled + + mkdir -p /tmp/mwan3rtmon + ($IP6 route list table main | grep -v "^default\|^::/0\|^fe80::/64\|^unreachable" | sort -n; echo empty fixup) >/tmp/mwan3rtmon/ipv6.main + while uci get mwan3.@interface[$idx] >/dev/null 2>&1 ; do + tid=$((idx+1)) + + family="$(uci -q get mwan3.@interface[$idx].family)" + # Set default family to ipv4 that is no mistake + [ -z "$family" ] && family="ipv4" + + enabled="$(uci -q get mwan3.@interface[$idx].enabled)" + [ -z "$enabled" ] && enabled="0" + + [ "$family" = "ipv6" ] && { + tbl=$($IP6 route list table $tid 2>/dev/null) + if echo "$tbl" | grep -q "^default\|^::/0"; then + (echo "$tbl" | grep -v "^default\|^::/0\|^unreachable" | sort -n; echo empty fixup) >/tmp/mwan3rtmon/ipv6.$tid + cat /tmp/mwan3rtmon/ipv6.$tid | grep -v -x -F -f /tmp/mwan3rtmon/ipv6.main | while read line; do + $IP6 route del table $tid $line + done + cat /tmp/mwan3rtmon/ipv6.main | grep -v -x -F -f /tmp/mwan3rtmon/ipv6.$tid | while read line; do + $IP6 route add table $tid $line + done + fi + } + if [ "$enabled" = "1" ]; then + ret=0 + fi + idx=$((idx+1)) + done + rm -f /tmp/mwan3rtmon/ipv6.* + return $ret } # counts how many bits are set to 1 @@ -103,12 +136,85 @@ mwan3_count_one_bits() 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< "${MWAN3_STATUS_DIR}/mmx_mask" + $LOG notice "Using firewall mask ${MMX_MASK}" + + bitcnt=$(mwan3_count_one_bits MMX_MASK) + mmdefault=$(((1<&1) || LOG error "set_custom_ipset: $error" + $IPS -! create mwan3_connected list:set + $IPS -! add mwan3_connected mwan3_custom_v4 + $IPS -! add mwan3_connected mwan3_custom_v6 } mwan3_set_connected_ipv4() { local connected_network_v4 candidate_list cidr_list - $IPS -! create mwan3_connected_v4 hash:net - $IPS create mwan3_connected_v4_temp hash:net || - LOG notice "failed to create ipset mwan3_connected_v4_temp" - candidate_list="" cidr_list="" - route_lists() - { - $IP4 route | awk '{print $1}' - $IP4 route list table 0 | awk '{print $2}' - } - for connected_network_v4 in $(route_lists | grep -E "$IPv4_REGEX"); do - if [ -z "${connected_network_v4##*/*}" ]; then - cidr_list="$cidr_list $connected_network_v4" - else - candidate_list="$candidate_list $connected_network_v4" - fi + for connected_network_v4 in $($IP4 route | awk '{print $1}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do + [ -z "${connected_network_v4##*/*}" ] && + cidr_list="$cidr_list $connected_network_v4" || + candidate_list="$candidate_list $connected_network_v4" + done + + for connected_network_v4 in $($IP4 route list table 0 | awk '{print $2}' | egrep '[0-9]{1,3}(\.[0-9]{1,3}){3}'); do + [ -z "${connected_network_v4##*/*}" ] && + cidr_list="$cidr_list $connected_network_v4" || + candidate_list="$candidate_list $connected_network_v4" done for connected_network_v4 in $cidr_list; do @@ -179,51 +284,50 @@ mwan3_set_connected_ipv4() $IPS -! add mwan3_connected_v4_temp "$connected_network_v4" done - $IPS add mwan3_connected_v4_temp 224.0.0.0/3 || - LOG notice "failed to add 224.0.0.0/3 to mwan3_connected_v4_temp" + $IPS add mwan3_connected_v4_temp 224.0.0.0/3 - $IPS swap mwan3_connected_v4_temp mwan3_connected_v4 || - LOG notice "failed to swap mwan3_connected_v4_temp and mwan3_connected_v4" - $IPS destroy mwan3_connected_v4_temp || - LOG notice "failed to destroy ipset mwan3_connected_v4_temp" + $IPS swap mwan3_connected_v4_temp mwan3_connected_v4 + $IPS destroy mwan3_connected_v4_temp + +} + +mwan3_set_connected_iptables() +{ + local connected_network_v6 source_network_v6 + + $IPS -! create mwan3_connected_v4 hash:net + $IPS create mwan3_connected_v4_temp hash:net + + mwan3_set_connected_ipv4 + + [ $NO_IPV6 -eq 0 ] && { + $IPS -! create mwan3_connected_v6 hash:net family inet6 + $IPS create mwan3_connected_v6_temp hash:net family inet6 + + for connected_network_v6 in $($IP6 route | awk '{print $1}' | egrep "$IPv6_REGEX"); do + $IPS -! add mwan3_connected_v6_temp "$connected_network_v6" + 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 + $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 + $IPS -! add mwan3_source_v6_temp "$source_network_v6" + done + $IPS swap mwan3_source_v6_temp mwan3_source_v6 + $IPS destroy mwan3_source_v6_temp + } + + $IPS -! create mwan3_connected list:set $IPS -! add mwan3_connected mwan3_connected_v4 + [ $NO_IPV6 -eq 0 ] && $IPS -! add mwan3_connected mwan3_connected_v6 -} + $IPS -! create mwan3_dynamic_v4 hash:net + $IPS -! add mwan3_connected mwan3_dynamic_v4 -mwan3_set_connected_ipv6() -{ - local connected_network_v6 error - local update="" - [ $NO_IPV6 -eq 0 ] || return - - mwan3_push_update -! create mwan3_connected_v6 hash:net family inet6 - mwan3_push_update flush mwan3_connected_v6 - - 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" - done - - mwan3_push_update -! add mwan3_connected mwan3_connected_v6 - error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipv6: $error" -} - -mwan3_set_connected_ipset() -{ - local error - local update="" - - mwan3_push_update -! create mwan3_connected list:set - mwan3_push_update flush mwan3_connected - - mwan3_push_update -! create mwan3_dynamic_v4 hash:net - mwan3_push_update -! add mwan3_connected mwan3_dynamic_v4 - - if [ $NO_IPV6 -eq 0 ]; then - mwan3_push_update -! create mwan3_dynamic_v6 hash:net family inet6 - mwan3_push_update -! add mwan3_connected mwan3_dynamic_v6 - fi - - error=$(echo "$update" | $IPS restore 2>&1) || LOG error "set_connected_ipset: $error" + [ $NO_IPV6 -eq 0 ] && $IPS -! create mwan3_dynamic_v6 hash:net family inet6 + [ $NO_IPV6 -eq 0 ] && $IPS -! add mwan3_connected mwan3_dynamic_v6 } mwan3_set_general_rules() @@ -232,12 +336,12 @@ mwan3_set_general_rules() for IP in "$IP4" "$IP6"; do [ "$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 $IP rule add pref $RULE_NO fwmark $MMX_BLACKHOLE/$MMX_MASK blackhole fi - RULE_NO=$((MM_UNREACHABLE+2000)) + RULE_NO=$(($MM_UNREACHABLE+2000)) 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 fi @@ -246,91 +350,89 @@ mwan3_set_general_rules() mwan3_set_general_iptables() { - local IPT current update error + local IPT + for IPT in "$IPT4" "$IPT6"; do [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue - current="$($IPT -S)"$'\n' - update="*mangle" - if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then - mwan3_push_update -N mwan3_ifaces_in + if ! $IPT -S mwan3_ifaces_in &> /dev/null; then + $IPT -N mwan3_ifaces_in fi - if [ -n "${current##*-N mwan3_connected*}" ]; then - mwan3_push_update -N mwan3_connected + if ! $IPT -S mwan3_connected &> /dev/null; then + $IPT -N mwan3_connected $IPS -! create mwan3_connected list:set - mwan3_push_update -A mwan3_connected \ - -m set --match-set mwan3_connected dst \ - -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK + $IPT -A mwan3_connected \ + -m set --match-set mwan3_connected dst \ + -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK fi - if [ -n "${current##*-N mwan3_rules*}" ]; then - mwan3_push_update -N mwan3_rules + if ! $IPT -S mwan3_rules &> /dev/null; then + $IPT -N mwan3_rules fi - if [ -n "${current##*-N mwan3_hook*}" ]; then - mwan3_push_update -N mwan3_hook + if ! $IPT -S mwan3_hook &> /dev/null; then + $IPT -N mwan3_hook # do not mangle ipv6 ra service if [ "$IPT" = "$IPT6" ]; then - mwan3_push_update -A mwan3_hook \ - -p ipv6-icmp \ - -m icmp6 --icmpv6-type 133 \ - -j RETURN - mwan3_push_update -A mwan3_hook \ - -p ipv6-icmp \ - -m icmp6 --icmpv6-type 134 \ - -j RETURN - mwan3_push_update -A mwan3_hook \ - -p ipv6-icmp \ - -m icmp6 --icmpv6-type 135 \ - -j RETURN - mwan3_push_update -A mwan3_hook \ - -p ipv6-icmp \ - -m icmp6 --icmpv6-type 136 \ - -j RETURN - mwan3_push_update -A mwan3_hook \ - -p ipv6-icmp \ - -m icmp6 --icmpv6-type 137 \ - -j RETURN + $IPT6 -A mwan3_hook \ + -p ipv6-icmp \ + -m icmp6 --icmpv6-type 133 \ + -j RETURN + $IPT6 -A mwan3_hook \ + -p ipv6-icmp \ + -m icmp6 --icmpv6-type 134 \ + -j RETURN + $IPT6 -A mwan3_hook \ + -p ipv6-icmp \ + -m icmp6 --icmpv6-type 135 \ + -j RETURN + $IPT6 -A mwan3_hook \ + -p ipv6-icmp \ + -m icmp6 --icmpv6-type 136 \ + -j RETURN + $IPT6 -A mwan3_hook \ + -p ipv6-icmp \ + -m icmp6 --icmpv6-type 137 \ + -j RETURN + # do not mangle outgoing echo request + $IPT6 -A mwan3_hook \ + -m set --match-set mwan3_source_v6 src \ + -p ipv6-icmp \ + -m icmp6 --icmpv6-type 128 \ + -j RETURN fi - mwan3_push_update -A mwan3_hook \ - -m mark --mark 0x0/$MMX_MASK \ - -j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" - mwan3_push_update -A mwan3_hook \ - -m mark --mark 0x0/$MMX_MASK \ - -j mwan3_ifaces_in - mwan3_push_update -A mwan3_hook \ - -m mark --mark 0x0/$MMX_MASK \ - -j mwan3_connected - mwan3_push_update -A mwan3_hook \ - -m mark --mark 0x0/$MMX_MASK \ - -j mwan3_rules - mwan3_push_update -A mwan3_hook \ - -j CONNMARK --save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" - mwan3_push_update -A mwan3_hook \ - -m mark ! --mark $MMX_DEFAULT/$MMX_MASK \ - -j mwan3_connected + $IPT -A mwan3_hook \ + -j CONNMARK --restore-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" + $IPT -A mwan3_hook \ + -m mark --mark 0x0/$MMX_MASK \ + -j mwan3_ifaces_in + $IPT -A mwan3_hook \ + -m mark --mark 0x0/$MMX_MASK \ + -j mwan3_connected + $IPT -A mwan3_hook \ + -m mark --mark 0x0/$MMX_MASK \ + -j mwan3_rules + $IPT -A mwan3_hook \ + -j CONNMARK --save-mark --nfmask "$MMX_MASK" --ctmask "$MMX_MASK" + $IPT -A mwan3_hook \ + -m mark ! --mark $MMX_DEFAULT/$MMX_MASK \ + -j mwan3_connected fi - if [ -n "${current##*-A PREROUTING -j mwan3_hook*}" ]; then - mwan3_push_update -A PREROUTING -j mwan3_hook + if ! $IPT -S PREROUTING | grep mwan3_hook &> /dev/null; then + $IPT -A PREROUTING -j mwan3_hook fi - if [ -n "${current##*-A OUTPUT -j mwan3_hook*}" ]; then - mwan3_push_update -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" + + if ! $IPT -S OUTPUT | grep mwan3_hook &> /dev/null; then + $IPT -A OUTPUT -j mwan3_hook fi done } mwan3_create_iface_iptables() { - local id family IPT IPTR current update error + local id family connected_name IPT config_get family "$1" family ipv4 mwan3_get_iface_id id "$1" @@ -338,51 +440,45 @@ mwan3_create_iface_iptables() [ -n "$id" ] || return 0 if [ "$family" = "ipv4" ]; then + connected_name=mwan3_connected IPT="$IPT4" - IPTR="$IPT4R" + $IPS -! create $connected_name list:set + elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then + connected_name=mwan3_connected_v6 IPT="$IPT6" - IPTR="$IPT6R" + $IPS -! create $connected_name hash:net family inet6 else return fi - current="$($IPT -S)"$'\n' - update="*mangle" - if [ -n "${current##*-N mwan3_ifaces_in*}" ]; then - mwan3_push_update -N mwan3_ifaces_in + if ! $IPT -S mwan3_ifaces_in &> /dev/null; then + $IPT -N mwan3_ifaces_in fi - if [ -n "${current##*-N mwan3_iface_in_$1$'\n'*}" ]; then - mwan3_push_update -N "mwan3_iface_in_$1" - else - mwan3_push_update -F "mwan3_iface_in_$1" + if ! $IPT -S "mwan3_iface_in_$1" &> /dev/null; then + $IPT -N "mwan3_iface_in_$1" fi - mwan3_push_update -A "mwan3_iface_in_$1" \ - -i "$2" \ - -m set --match-set mwan3_connected src \ - -m mark --mark "0x0/$MMX_MASK" \ - -m comment --comment "default" \ - -j MARK --set-xmark "$MMX_DEFAULT/$MMX_MASK" - mwan3_push_update -A "mwan3_iface_in_$1" \ - -i "$2" \ - -m mark --mark "0x0/$MMX_MASK" \ - -m comment --comment "$1" \ - -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" + $IPT -F "mwan3_iface_in_$1" + $IPT -A "mwan3_iface_in_$1" \ + -i "$2" \ + -m set --match-set $connected_name src \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "default" \ + -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK + $IPT -A "mwan3_iface_in_$1" \ + -i "$2" \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "$1" \ + -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK - 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 \ - -m mark --mark 0x0/$MMX_MASK \ - -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" + $IPT -D mwan3_ifaces_in \ + -m mark --mark 0x0/$MMX_MASK \ + -j "mwan3_iface_in_$1" &> /dev/null + $IPT -A mwan3_ifaces_in \ + -m mark --mark 0x0/$MMX_MASK \ + -j "mwan3_iface_in_$1" } @@ -408,71 +504,67 @@ mwan3_delete_iface_iptables() } -mwan3_extra_tables_routes() -{ - $IP route list table "$1" -} - -mwan3_get_routes() -{ - { - $IP route list table main - config_list_foreach "globals" "rt_table_lookup" mwan3_extra_tables_routes - } | sed -ne "$MWAN3_ROUTE_LINE_EXP" | sort -u -} - mwan3_create_iface_route() { - local tid route_line family IP id tbl + local id via metric V V_ IP + config_get family "$1" family ipv4 mwan3_get_iface_id id "$1" [ -n "$id" ] || return 0 if [ "$family" = "ipv4" ]; then + V=4 + V_="" IP="$IP4" elif [ "$family" = "ipv6" ]; then + V=6 + V_=6 IP="$IP6" + else + return fi - tbl=$($IP route list table $id 2>/dev/null)$'\n' - mwan3_update_dev_to_table - mwan3_get_routes | while read -r route_line; do - 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 - # 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 || - LOG warn "failed to add $route_line to table $id" - fi + if ubus call network.interface.${1}_${V} status &>/dev/null; then + network_get_gateway${V_} via "${1}_${V}" + else + network_get_gateway${V_} via "$1" + fi + + ( [ -z "$via" ] || [ "$via" = "0.0.0.0" ] || [ "$via" = "::" ] ) && unset via + + network_get_metric metric "$1" + + $IP route flush table "$id" + $IP route add table "$id" default \ + ${via:+via} $via \ + ${metric:+metric} $metric \ + dev "$2" + mwan3_rtmon_ipv${V} - done } mwan3_delete_iface_route() { - local id family + local id config_get family "$1" family ipv4 mwan3_get_iface_id id "$1" - if [ -z "$id" ]; then - LOG warn "delete_iface_route: could not find table id for interface $1" - return 0 - fi + [ -n "$id" ] || return 0 if [ "$family" = "ipv4" ]; then $IP4 route flush table "$id" - elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then + fi + + if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then $IP6 route flush table "$id" fi } mwan3_create_iface_rules() { - local id family IP + local id family config_get family "$1" family ipv4 mwan3_get_iface_id id "$1" @@ -480,23 +572,37 @@ mwan3_create_iface_rules() [ -n "$id" ] || return 0 if [ "$family" = "ipv4" ]; then - IP="$IP4" - elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then - IP="$IP6" - else - return + + while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do + $IP4 rule del pref $(($id+1000)) + done + + 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 - mwan3_delete_iface_rules "$1" + if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then - $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" - $IP rule add pref $((id+3000)) fwmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" unreachable + while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do + $IP6 rule del pref $(($id+1000)) + done + + while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do + $IP6 rule del pref $(($id+2000)) + done + + $IP6 rule add pref $(($id+1000)) iif "$2" lookup "$id" + $IP6 rule add pref $(($id+2000)) fwmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK lookup "$id" + fi } mwan3_delete_iface_rules() { - local id family IP rule_id + local id family config_get family "$1" family ipv4 mwan3_get_iface_id id "$1" @@ -504,16 +610,26 @@ mwan3_delete_iface_rules() [ -n "$id" ] || return 0 if [ "$family" = "ipv4" ]; then - IP="$IP4" - elif [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then - IP="$IP6" - else - return + + while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do + $IP4 rule del pref $(($id+1000)) + done + + while [ -n "$($IP4 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do + $IP4 rule del pref $(($id+2000)) + done fi - 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 $rule_id - done + if [ "$family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then + + while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+1000)):'"')" ]; do + $IP6 rule del pref $(($id+1000)) + done + + while [ -n "$($IP6 rule list | awk '$1 == "'$(($id+2000)):'"')" ]; do + $IP6 rule del pref $(($id+2000)) + done + fi } mwan3_delete_iface_ipset_entries() @@ -525,17 +641,57 @@ mwan3_delete_iface_ipset_entries() [ -n "$id" ] || return 0 for setname in $(ipset -n list | grep ^mwan3_sticky_); 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 || - LOG notice "failed to delete $entry from $setname" + for entry in $(ipset list "$setname" | grep "$(echo $(mwan3_id2mask id MMX_MASK) | awk '{ printf "0x%08x", $1; }')" | cut -d ' ' -f 1); do + $IPS del "$setname" $entry done done } +mwan3_rtmon() +{ + pid="$(pgrep -f mwan3rtmon)" + if [ "${pid}" != "" ]; then + kill -USR1 "${pid}" + else + [ -x /usr/sbin/mwan3rtmon ] && /usr/sbin/mwan3rtmon & + fi +} + +mwan3_track() +{ + local track_ip track_ips pid + + mwan3_list_track_ips() + { + track_ips="$track_ips $1" + } + config_list_foreach "$1" track_ip mwan3_list_track_ips + + for pid in $(pgrep -f "mwan3track $1 $2"); do + kill -TERM "$pid" > /dev/null 2>&1 + done + sleep 1 + for pid in $(pgrep -f "mwan3track $1 $2"); do + kill -KILL "$pid" > /dev/null 2>&1 + done + if [ -n "$track_ips" ]; then + [ -x /usr/sbin/mwan3track ] && /usr/sbin/mwan3track "$1" "$2" "$3" "$4" $track_ips & + fi +} + +mwan3_track_signal() +{ + local pid + + pid="$(pgrep -f "mwan3track $1 $2")" + [ "${pid}" != "" ] && { + kill -USR1 "${pid}" + } +} mwan3_set_policy() { - local id iface family metric probability weight device is_lowest is_offline IPT IPTR total_weight current update error + local iface_count id iface family metric probability weight device is_lowest is_offline IPT total_weight is_lowest=0 config_get iface "$1" interface @@ -544,26 +700,22 @@ mwan3_set_policy() [ -n "$iface" ] || return 0 network_get_device device "$iface" - [ "$metric" -gt $DEFAULT_LOWEST_METRIC ] && LOG warn "Member interface $iface has >$DEFAULT_LOWEST_METRIC metric. Not appending to policy" && return 0 + [ "$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" - [ -n "$id" ] || return 0 - [ "$(mwan3_get_iface_hotplug_state "$iface")" = "online" ] is_offline=$? + [ -n "$id" ] || return 0 + config_get family "$iface" family ipv4 if [ "$family" = "ipv4" ]; then IPT="$IPT4" - IPTR="$IPT4R" elif [ "$family" = "ipv6" ]; then IPT="$IPT6" - IPTR="$IPT6R" fi - current="$($IPT -S)"$'\n' - update="*mangle" if [ "$family" = "ipv4" ] && [ $is_offline -eq 0 ]; then if [ "$metric" -lt "$lowest_metric_v4" ]; then @@ -571,7 +723,7 @@ mwan3_set_policy() total_weight_v4=$weight lowest_metric_v4=$metric 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 else return @@ -582,20 +734,20 @@ mwan3_set_policy() total_weight_v6=$weight lowest_metric_v6=$metric 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 else return fi fi if [ $is_lowest -eq 1 ]; then - mwan3_push_update -F "mwan3_policy_$policy" - mwan3_push_update -A "mwan3_policy_$policy" \ - -m mark --mark 0x0/$MMX_MASK \ - -m comment --comment \"$iface $weight $weight\" \ - -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" + $IPT -F "mwan3_policy_$policy" + $IPT -A "mwan3_policy_$policy" \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "$iface $weight $weight" \ + -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK elif [ $is_offline -eq 0 ]; then - probability=$((weight*1000/total_weight)) + probability=$(($weight*1000/$total_weight)) if [ "$probability" -lt 10 ]; then probability="0.00$probability" elif [ $probability -lt 100 ]; then @@ -605,77 +757,64 @@ mwan3_set_policy() else probability="1" fi - - mwan3_push_update -I "mwan3_policy_$policy" \ - -m mark --mark 0x0/$MMX_MASK \ - -m statistic \ - --mode random \ - --probability "$probability" \ - -m comment --comment \"$iface $weight $total_weight\" \ - -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" + + $IPT -I "mwan3_policy_$policy" \ + -m mark --mark 0x0/$MMX_MASK \ + -m statistic \ + --mode random \ + --probability "$probability" \ + -m comment --comment "$iface $weight $total_weight" \ + -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK elif [ -n "$device" ]; then - echo "$current" | grep -q "^-A mwan3_policy_$policy.*--comment .* [0-9]* [0-9]*" || - mwan3_push_update -I "mwan3_policy_$policy" \ - -o "$device" \ - -m mark --mark 0x0/$MMX_MASK \ - -m comment --comment \"out $iface $device\" \ - -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK + $IPT -S "mwan3_policy_$policy" | grep -q '.*--comment ".* [0-9]* [0-9]*"' || \ + $IPT -I "mwan3_policy_$policy" \ + -o "$device" \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "out $iface $device" \ + -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK fi - mwan3_push_update COMMIT - mwan3_push_update "" - error=$(echo "$update" | $IPTR 2>&1) || LOG error "set_policy ($1): $error" - } mwan3_create_policies_iptables() { - local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT current update error + local last_resort lowest_metric_v4 lowest_metric_v6 total_weight_v4 total_weight_v6 policy IPT policy="$1" config_get last_resort "$1" last_resort unreachable 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 for IPT in "$IPT4" "$IPT6"; do [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue - current="$($IPT -S)"$'\n' - update="*mangle" - if [ -n "${current##*-N mwan3_policy_$1$'\n'*}" ]; then - mwan3_push_update -N "mwan3_policy_$1" + if ! $IPT -S "mwan3_policy_$1" &> /dev/null; then + $IPT -N "mwan3_policy_$1" fi - mwan3_push_update -F "mwan3_policy_$1" + $IPT -F "mwan3_policy_$1" case "$last_resort" in blackhole) - mwan3_push_update -A "mwan3_policy_$1" \ - -m mark --mark 0x0/$MMX_MASK \ - -m comment --comment "blackhole" \ - -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK - ;; + $IPT -A "mwan3_policy_$1" \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "blackhole" \ + -j MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK + ;; default) - mwan3_push_update -A "mwan3_policy_$1" \ - -m mark --mark 0x0/$MMX_MASK \ - -m comment --comment "default" \ - -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK - ;; + $IPT -A "mwan3_policy_$1" \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "default" \ + -j MARK --set-xmark $MMX_DEFAULT/$MMX_MASK + ;; *) - mwan3_push_update -A "mwan3_policy_$1" \ - -m mark --mark 0x0/$MMX_MASK \ - -m comment --comment "unreachable" \ - -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK - ;; + $IPT -A "mwan3_policy_$1" \ + -m mark --mark 0x0/$MMX_MASK \ + -m comment --comment "unreachable" \ + -j MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK + ;; 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 lowest_metric_v4=$DEFAULT_LOWEST_METRIC @@ -695,21 +834,27 @@ mwan3_set_policies_iptables() mwan3_set_sticky_iptables() { 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 mwan3_get_iface_id id "$1" [ -n "$id" ] || return 0 - if [ -z "${current##*-N mwan3_iface_in_$1$'\n'*}" ]; then - mwan3_push_update -I "mwan3_rule_$rule" \ - -m mark --mark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" \ - -m set ! --match-set "mwan3_sticky_$rule" src,src \ - -j MARK --set-xmark "0x0/$MMX_MASK" - mwan3_push_update -I "mwan3_rule_$rule" \ - -m mark --mark "0/$MMX_MASK" \ - -j MARK --set-xmark "$(mwan3_id2mask id MMX_MASK)/$MMX_MASK" - fi + + for IPT in "$IPT4" "$IPT6"; do + [ "$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 set ! --match-set "mwan3_sticky_$rule" src,src \ + -j MARK --set-xmark 0x0/$MMX_MASK + $IPT -I "mwan3_rule_$rule" \ + -m mark --mark 0/$MMX_MASK \ + -j MARK --set-xmark $(mwan3_id2mask id MMX_MASK)/$MMX_MASK + fi + done fi done } @@ -717,294 +862,161 @@ mwan3_set_sticky_iptables() mwan3_set_user_iptables_rule() { local ipset family proto policy src_ip src_port src_iface src_dev - local sticky dest_ip dest_port use_policy timeout policy - local global_logging rule_logging loglevel rule_policy rule ipv + local sticky dest_ip dest_port use_policy timeout rule policy IPT + local global_logging rule_logging loglevel rule="$1" - ipv="$2" - rule_policy=0 + config_get sticky "$1" sticky 0 config_get timeout "$1" timeout 600 config_get ipset "$1" ipset config_get proto "$1" proto all config_get src_ip "$1" src_ip config_get src_iface "$1" src_iface + network_get_device src_dev "$src_iface" config_get src_port "$1" src_port config_get dest_ip "$1" dest_ip config_get dest_port "$1" dest_port config_get use_policy "$1" use_policy 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 - - [ "$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 - 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 "$src_ip" ] && unset src_ip [ -z "$ipset" ] && unset ipset - [ -z "$src_port" ] && unset src_port - [ -z "$dest_port" ] && unset dest_port - if [ "$proto" != 'tcp' ] && [ "$proto" != 'udp' ]; then + [ -z "$src_port" ] && unset src_port + [ -z "$dest_port" ] && unset dest_port + [ "$proto" != 'tcp' ] && [ "$proto" != 'udp' ] && { [ -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" ] && { - 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 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 - 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 if [ -n "$ipset" ]; then ipset="-m set --match-set $ipset dst" fi - if [ -z "$use_policy" ]; then - return - fi + if [ -n "$use_policy" ]; then + if [ "$use_policy" = "default" ]; then + policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK" + elif [ "$use_policy" = "unreachable" ]; then + policy="MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK" + elif [ "$use_policy" = "blackhole" ]; then + policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK" + else + if [ "$sticky" -eq 1 ]; then - if [ "$use_policy" = "default" ]; then - policy="MARK --set-xmark $MMX_DEFAULT/$MMX_MASK" - elif [ "$use_policy" = "unreachable" ]; then - policy="MARK --set-xmark $MMX_UNREACHABLE/$MMX_MASK" - elif [ "$use_policy" = "blackhole" ]; then - policy="MARK --set-xmark $MMX_BLACKHOLE/$MMX_MASK" - else - rule_policy=1 - policy="mwan3_policy_$use_policy" - if [ "$sticky" -eq 1 ]; then - $IPS -! create "mwan3_sticky_v4_$rule" \ - hash:ip,mark markmask "$MMX_MASK" \ - timeout "$timeout" - [ $NO_IPV6 -eq 0 ] && + 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 + + 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" \ + hash:ip,mark markmask "$MMX_MASK" \ + timeout "$timeout" $IPS -! create "mwan3_sticky_v6_$rule" \ - hash:ip,mark markmask "$MMX_MASK" \ - timeout "$timeout" family inet6 - $IPS -! create "mwan3_sticky_$rule" list:set - $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v4_$rule" - [ $NO_IPV6 -eq 0 ] && + hash:ip,mark markmask "$MMX_MASK" \ + timeout "$timeout" family inet6 + $IPS -! create "mwan3_sticky_$rule" list:set + $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v4_$rule" $IPS -! add "mwan3_sticky_$rule" "mwan3_sticky_v6_$rule" + + config_foreach mwan3_set_sticky_iptables interface + + for IPT in "$IPT4" "$IPT6"; do + [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue + $IPT -A "mwan3_rule_$1" \ + -m mark --mark 0/$MMX_MASK \ + -j "$policy" + $IPT -A "mwan3_rule_$1" \ + -m mark ! --mark 0xfc00/0xfc00 \ + -j SET --del-set "mwan3_sticky_$rule" src,src + $IPT -A "mwan3_rule_$1" \ + -m mark ! --mark 0xfc00/0xfc00 \ + -j SET --add-set "mwan3_sticky_$rule" src,src + done + + 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 + done + + 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" \ + ${src_ip:+-s} $src_ip \ + ${src_dev:+-i} $src_dev \ + ${dest_ip:+-d} $dest_ip \ + $ipset \ + ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \ + ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \ + -m mark --mark 0/$MMX_MASK \ + -m comment --comment "$1" \ + -j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)" + } + + $IPT -A mwan3_rules \ + -p "$proto" \ + ${src_ip:+-s} $src_ip \ + ${src_dev:+-i} $src_dev \ + ${dest_ip:+-d} $dest_ip \ + $ipset \ + ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \ + ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \ + -m mark --mark 0/$MMX_MASK \ + -j $policy + done fi - - if [ $rule_policy -eq 1 ] && [ -n "${current##*-N $policy$'\n'*}" ]; then - mwan3_push_update -N "$policy" - fi - - if [ $rule_policy -eq 1 ] && [ "$sticky" -eq 1 ]; then - if [ -n "${current##*-N mwan3_rule_$1$'\n'*}" ]; 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 \ - -j "$policy" - mwan3_push_update -A "mwan3_rule_$1" \ - -m mark ! --mark 0xfc00/0xfc00 \ - -j SET --del-set "mwan3_sticky_$rule" src,src - mwan3_push_update -A "mwan3_rule_$1" \ - -m mark ! --mark 0xfc00/0xfc00 \ - -j SET --add-set "mwan3_sticky_$rule" src,src - policy="mwan3_rule_$1" - fi - if [ "$global_logging" = "1" ] && [ "$rule_logging" = "1" ]; then - mwan3_push_update -A mwan3_rules \ - -p "$proto" \ - ${src_ip:+-s} $src_ip \ - ${src_dev:+-i} $src_dev \ - ${dest_ip:+-d} $dest_ip \ - $ipset \ - ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \ - ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \ - -m mark --mark 0/$MMX_MASK \ - -m comment --comment "$1" \ - -j LOG --log-level "$loglevel" --log-prefix "MWAN3($1)" - fi - - mwan3_push_update -A mwan3_rules \ - -p "$proto" \ - ${src_ip:+-s} $src_ip \ - ${src_dev:+-i} $src_dev \ - ${dest_ip:+-d} $dest_ip \ - $ipset \ - ${src_port:+-m} ${src_port:+multiport} ${src_port:+--sports} $src_port \ - ${dest_port:+-m} ${dest_port:+multiport} ${dest_port:+--dports} $dest_port \ - -m mark --mark 0/$MMX_MASK \ - -j $policy - -} - -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 - - 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() { - local IPT IPTR ipv - local current update error + local IPT - for ipv in ipv4 ipv6; do - if [ "$ipv" = "ipv4" ]; then - IPT="$IPT4" - IPTR="$IPT4R" - elif [ "$ipv" = "ipv6" ]; then - IPT="$IPT6" - IPTR="$IPT6R" - fi - [ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && continue - update="*mangle" - current="$($IPT -S)"$'\n' - - - if [ -n "${current##*-N mwan3_rules*}" ]; then - mwan3_push_update -N "mwan3_rules" + for IPT in "$IPT4" "$IPT6"; do + [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue + if ! $IPT -S mwan3_rules &> /dev/null; then + $IPT -N mwan3_rules fi - 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" + $IPT -F mwan3_rules done - -} - -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 interface=$1 - local caller=$2 - - local up l3_device status true_iface - - if [ "${caller}" = "cmd" ]; 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=$caller ACTION=ifup \ - INTERFACE=$interface DEVICE=$l3_device \ - sh /etc/hotplug.d/iface/15-mwan3 - } - - if [ "$up" != "1" ] || [ -z "$l3_device" ]; then - return - fi - - if [ "${caller}" = "init" ]; then - hotplug_startup & - hotplug_pids="$hotplug_pids $!" - else - hotplug_startup - fi - + config_foreach mwan3_set_user_iptables_rule rule } mwan3_set_iface_hotplug_state() { @@ -1022,7 +1034,7 @@ mwan3_get_iface_hotplug_state() { mwan3_report_iface_status() { - local device result tracking IP IPT error + local device result track_ips tracking IP IPT mwan3_get_iface_id id "$1" network_get_device device "$1" @@ -1041,37 +1053,49 @@ mwan3_report_iface_status() if [ -z "$id" ] || [ -z "$device" ]; then result="offline" - else - error=0 - [ -n "$($IP rule | awk '$1 == "'$((id+1000)):'"')" ] || - error=$((error+1)) - [ -n "$($IP rule | awk '$1 == "'$((id+2000)):'"')" ] || - error=$((error+2)) - [ -n "$($IP rule | awk '$1 == "'$((id+3000)):'"')" ] || - error=$((error+4)) - [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] || - error=$((error+8)) - [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ] || - error=$((error+16)) - fi - - if [ "$result" = "offline" ]; then - : - elif [ $error -eq 0 ]; then - online=$(get_online_time "$1") - network_get_uptime uptime "$1" - 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)))" + elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" ] && \ + [ -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" ] && \ + [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] && \ + [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then + json_init + json_add_string section interfaces + json_add_string interface "$1" + json_load "$(ubus call mwan3 status "$(json_dump)")" + json_select "interfaces" + json_select "$1" + json_get_vars online uptime + json_select .. + json_select .. + 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)))" result="$(mwan3_get_iface_hotplug_state $1) $online, uptime $uptime" - elif [ $error -gt 0 ] && [ $error -ne 31 ]; then - result="error (${error})" + elif [ -n "$($IP rule | awk '$1 == "'$(($id+1000)):'"')" ] || \ + [ -n "$($IP rule | awk '$1 == "'$(($id+2000)):'"')" ] || \ + [ -n "$($IPT -S mwan3_iface_in_$1 2> /dev/null)" ] || \ + [ -n "$($IP route list table $id default dev $device 2> /dev/null)" ]; then + result="error" elif [ "$enabled" = "1" ]; then result="offline" else result="disabled" fi - tracking="$(mwan3_get_mwan3track_status $1)" + mwan3_list_track_ips() + { + 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" } @@ -1084,10 +1108,10 @@ mwan3_report_policies() total_weight=$($ipt -S "$policy" | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}') - if [ -n "${total_weight##*[!0-9]*}" ]; then + if [ ! -z "${total_weight##*[!0-9]*}" ]; then 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}') - percent=$((weight*100/total_weight)) + percent=$(($weight*100/$total_weight)) echo " $iface ($percent%)" done else @@ -1154,7 +1178,7 @@ mwan3_flush_conntrack() if [ "$action" = "$flush_conntrack" ]; then 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 } @@ -1165,6 +1189,10 @@ mwan3_flush_conntrack() mwan3_track_clean() { - rm -rf "${MWAN3_STATUS_DIR:?}/${1}" &> /dev/null - rmdir --ignore-fail-on-non-empty "$MWAN3_STATUS_DIR" + rm -rf "$MWAN3_STATUS_DIR/${1}" &> /dev/null + [ -d "$MWAN3_STATUS_DIR" ] && { + if [ -z "$(ls -A "$MWAN3_STATUS_DIR")" ]; then + rm -rf "$MWAN3_STATUS_DIR" + fi + } } diff --git a/mwan3/files/usr/libexec/rpcd/mwan3 b/mwan3/files/usr/libexec/rpcd/mwan3 index 4c71fa18f..33e3e0284 100755 --- a/mwan3/files/usr/libexec/rpcd/mwan3 +++ b/mwan3/files/usr/libexec/rpcd/mwan3 @@ -5,6 +5,8 @@ . /usr/share/libubox/jshn.sh . /lib/mwan3/common.sh +MWAN3TRACK_STATUS_DIR="/var/run/mwan3track" + IPS="ipset" IPT4="iptables -t mangle -w" IPT6="ip6tables -t mangle -w" @@ -20,7 +22,6 @@ report_connected_v4() { } report_connected_v6() { - [ $NO_IPV6 -ne 0 ] && return local address if [ -n "$($IPT6 -S mwan3_connected 2> /dev/null)" ]; then @@ -59,7 +60,6 @@ report_policies_v4() { } report_policies_v6() { - [ $NO_IPV6 -ne 0 ] && return local policy for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do @@ -69,26 +69,6 @@ report_policies_v6() { done } -get_age() { - local time_p time_u - iface="$1" - time_p="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/TIME")" - [ -z "${time_p}" ] || { - time_n="$(get_uptime)" - echo $((time_n-time_p)) - } -} - -get_offline_time() { - local time_n time_d iface - iface="$1" - time_d="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/OFFLINE")" - [ -z "${time_d}" ] || [ "${time_d}" = "0" ] || { - time_n="$(get_uptime)" - echo $((time_n-time_d)) - } -} - get_mwan3_status() { local iface="${1}" local iface_select="${2}" @@ -96,60 +76,74 @@ get_mwan3_status() { local age=0 local online=0 local offline=0 - local enabled time_p time_n time_u time_d status track_status up uptime + local up="0" + local enabled pid device time_p time_n time_u time_d status - if [ "${iface}" != "${iface_select}" ] && [ "${iface_select}" != "" ]; then - return - fi + network_get_device device $1 - track_status="$(mwan3_get_mwan3track_status "$1")" - [ "$track_status" = "active" ] && running="1" - age=$(get_age "$iface") - online=$(get_online_time "$iface") - offline=$(get_offline_time "$iface") + if [ "${iface}" = "${iface_select}" ] || [ "${iface_select}" = "" ]; then + pid="$(pgrep -f "mwan3track $iface $device")" + if [ "${pid}" != "" ]; then + running="1" + fi - config_get enabled "$iface" enabled 0 + time_p="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/TIME")" + [ -z "${time_p}" ] || { + time_n="$(get_uptime)" + let age=time_n-time_p + } - if [ -d "${MWAN3_STATUS_DIR}" ]; then + time_u="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/ONLINE")" + [ -z "${time_u}" ] || [ "${time_u}" = "0" ] || { + time_n="$(get_uptime)" + let online=time_n-time_u + } + + time_d="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/OFFLINE")" + [ -z "${time_d}" ] || [ "${time_d}" = "0" ] || { + time_n="$(get_uptime)" + let offline=time_n-time_d + } + + local uptime="0" + + config_get enabled "$iface" enabled 0 network_get_uptime uptime "$iface" network_is_up "$iface" && up="1" + if [ -f "$MWAN3TRACK_STATUS_DIR/${iface}/STATUS" ]; then status="$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/STATUS")" else - status="notracking" + status="unknown" fi - else - uptime=0 - up=0 - status="unknown" - fi - json_add_object "${iface}" - json_add_int age "$age" - json_add_int online "${online}" - json_add_int offline "${offline}" - json_add_int uptime "${uptime}" - json_add_int "score" "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/SCORE")" - json_add_int "lost" "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/LOST")" - json_add_int "turn" "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/TURN")" - json_add_string "status" "${status}" - json_add_boolean "enabled" "${enabled}" - json_add_boolean "running" "${running}" - json_add_boolean "up" "${up}" - json_add_array "track_ip" - for file in $MWAN3TRACK_STATUS_DIR/${iface}/TRACK_*; do - [ -z "${file#*/TRACK_OUTPUT}" ] && continue - [ -z "${file#*/TRACK_\*}" ] && continue - track="${file#*/TRACK_}" - json_add_object - json_add_string ip "${track}" - json_add_string status "$(cat "${file}")" - json_add_int latency "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/LATENCY_${track}")" - json_add_int packetloss "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/LOSS_${track}")" + json_add_object "${iface}" + json_add_int age "$age" + json_add_int online "${online}" + json_add_int offline "${offline}" + json_add_int uptime "${uptime}" + json_add_int "score" "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/SCORE")" + json_add_int "lost" "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/LOST")" + json_add_int "turn" "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/TURN")" + json_add_string "status" "${status}" + json_add_boolean "enabled" "${enabled}" + json_add_boolean "running" "${running}" + json_add_boolean "up" "${up}" + json_add_array "track_ip" + for file in $MWAN3TRACK_STATUS_DIR/${iface}/*; do + track="${file#*/TRACK_}" + if [ "${track}" != "${file}" ]; then + json_add_object + json_add_string ip "${track}" + json_add_string status "$(cat "${file}")" + json_add_int latency "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/LATENCY_${track}")" + json_add_int packetloss "$(cat "$MWAN3TRACK_STATUS_DIR/${iface}/LOSS_${track}")" + json_close_object + fi + done + json_close_array json_close_object - done - json_close_array - json_close_object + fi } main () { diff --git a/mwan3/files/usr/sbin/mwan3 b/mwan3/files/usr/sbin/mwan3 index 1ba6004bb..79a0eba25 100755 --- a/mwan3/files/usr/sbin/mwan3 +++ b/mwan3/files/usr/sbin/mwan3 @@ -4,14 +4,6 @@ . /usr/share/libubox/jshn.sh . /lib/functions/network.sh . /lib/mwan3/mwan3.sh -. /lib/mwan3/common.sh - -command_help() { - local cmd="$1" - local help="$2" - - echo "$(printf "%-25s%s" "${cmd}" "${help}")" -} help() { @@ -19,49 +11,78 @@ help() Syntax: mwan3 [command] Available commands: + start Load iptables rules, ip rules and ip routes + stop Unload iptables rules, ip rules and ip routes + restart Reload iptables rules, ip rules and ip routes + ifup Load rules and routes for specific interface + ifdown Unload rules and routes for specific interface + interfaces Show interfaces status + policies Show currently active policy + connected Show directly connected networks + rules Show active rules + status Show all status + EOF - command_help "start" "Load iptables rules, ip rules and ip routes" - command_help "stop" "Unload iptables rules, ip rules and ip routes" - command_help "restart" "Reload iptables rules, ip rules and ip routes" - command_help "ifup " "Load rules and routes for specific interface" - command_help "ifdown " "Unload rules and routes for specific interface" - command_help "interfaces" "Show interfaces status" - command_help "policies" "Show currently active policy" - command_help "connected" "Show directly connected networks" - command_help "rules" "Show active rules" - command_help "status" "Show all status" - command_help "internal " "Show internal configuration " - command_help "use " "Run a command bound to and avoid mwan3 rules" } -ifdown() { +ifdown() +{ if [ -z "$1" ]; then - echo "Error: Expecting interface. Usage: mwan3 ifdown " - exit 0 + echo "Error: Expecting interface. Usage: mwan3 ifdown " && exit 0 fi if [ -n "$2" ]; then - echo "Error: Too many arguments. Usage: mwan3 ifdown " - exit 0 + echo "Error: Too many arguments. Usage: mwan3 ifdown " && exit 0 fi - mwan3_interface_hotplug_shutdown "$1" 1 + ACTION=ifdown INTERFACE=$1 /sbin/hotplug-call iface + + kill $(pgrep -f "mwan3track $1 $2") &> /dev/null + mwan3_track_clean $1 } -ifup() { - . /etc/init.d/mwan3 +ifup() +{ + local device enabled up l3_device status + + mwan3_lock "command" "mwan3" + + 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." + mwan3_unlock "command" "mwan3" + exit 1 + } if [ -z "$1" ]; then echo "Expecting interface. Usage: mwan3 ifup " + mwan3_unlock "command" "mwan3" exit 0 fi if [ -n "$2" ]; then echo "Too many arguments. Usage: mwan3 ifup " + mwan3_unlock "command" "mwan3" exit 0 fi - mwan3_ifup "$1" "cmd" + config_get enabled "$1" enabled 0 + mwan3_unlock "command" "mwan3" + + status=$(ubus -S call network.interface.$1 status) + [ -n "$status" ] && { + json_load "$status" + json_get_vars up l3_device + } + + if [ "$up" = "1" ] \ + && [ -n "$l3_device" ] \ + && [ "$enabled" = "1" ]; then + ACTION=ifup INTERFACE=$1 DEVICE=$l3_device /sbin/hotplug-call iface + fi } interfaces() @@ -70,40 +91,40 @@ interfaces() echo "Interface status:" config_foreach mwan3_report_iface_status interface - echo + echo -e } policies() { echo "Current ipv4 policies:" mwan3_report_policies_v4 - echo + echo -e [ $NO_IPV6 -ne 0 ] && return echo "Current ipv6 policies:" mwan3_report_policies_v6 - echo + echo -e } connected() { echo "Directly connected ipv4 networks:" mwan3_report_connected_v4 - echo + echo -e [ $NO_IPV6 -ne 0 ] && return echo "Directly connected ipv6 networks:" mwan3_report_connected_v6 - echo + echo -e } rules() { echo "Active ipv4 user rules:" mwan3_report_rules_v4 - echo + echo -e [ $NO_IPV6 -ne 0 ] && return echo "Active ipv6 user rules:" mwan3_report_rules_v6 - echo + echo -e } status() @@ -114,136 +135,92 @@ status() rules } -internal() +start() { - local family="$1" - local dash="-------------------------------------------------" + local enabled - if [ -f "/etc/openwrt_release" ]; then - . /etc/openwrt_release - fi + mwan3_lock "command" "mwan3" + uci_toggle_state mwan3 globals enabled "1" + mwan3_unlock "command" "mwan3" - local ipt ip output + config_load mwan3 + config_foreach ifup interface +} - if [ "$family" = "ipv6" ]; then - ipt="$IPT6" - ip="$IP6" - else - ipt="$IPT4" - ip="$IP4" - fi +stop() +{ + local ipset route rule table IP IPT pid - echo "Software-Version" - echo "$dash" + mwan3_lock "command" "mwan3" + uci_toggle_state mwan3 globals enabled "0" - if [ "$DISTRIB_RELEASE" != "" ]; then - echo "OpenWrt - $DISTRIB_RELEASE" - else - echo "OpenWrt - unknown" - fi - - echo "" - echo "Output of \"$ip a show\"" - echo "$dash" - output="$($ip a show)" - if [ "$output" != "" ]; then - echo "$output" - else - echo "No data found" - fi - - echo "" - echo "Output of \"$ip route show\"" - echo "$dash" - output="$($ip route show)" - if [ "$output" != "" ]; then - echo "$output" - else - echo "No data found" - fi - - echo "" - echo "Output of \"$ip rule show\"" - echo "$dash" - output="$($ip rule show)" - if [ "$output" != "" ]; then - echo "$output" - else - echo "No data found" - fi - - echo "" - echo "Output of \"$ip route list table 1-250\"" - echo "$dash" - local dump=0 - for i in $(seq 1 250); do - output=$($ip route list table $i 2>/dev/null) - if [ "$output" != "" ];then - dump=1 - echo "Routing table $i:" - echo "$output" - echo "" - fi + for pid in $(pgrep -f "mwan3rtmon"); do + kill -TERM "$pid" > /dev/null 2>&1 done - if [ "$dump" = "0" ]; then - echo "No data found" - echo "" - fi - echo "Output of \"$ipt -L -v -n\"" - echo "$dash" - output="$($ipt -L -v -n)" - if [ "$output" != "" ]; then - echo "$output" - else - echo "No data found" - fi -} + for pid in $(pgrep -f "mwan3track"); do + kill -TERM "$pid" > /dev/null 2>&1 + done -start() { - /etc/init.d/mwan3 enable - /etc/init.d/mwan3 start -} + sleep 1 -stop() { - /etc/init.d/mwan3 disable - /etc/init.d/mwan3 stop + for pid in $(pgrep -f "mwan3rtmon"); do + kill -KILL "$pid" > /dev/null 2>&1 + done + + for pid in $(pgrep -f "mwan3track"); do + kill -KILL "$pid" > /dev/null 2>&1 + done + + config_load mwan3 + config_foreach mwan3_track_clean interface + + for IP in "$IP4" "$IP6"; do + [ "$IP" = "$IP6" ] && [ $NO_IPV6 -ne 0 ] && continue + for route in $(seq 1 $MWAN3_INTERFACE_MAX); do + $IP route flush table $route &> /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 + done + + for IPT in "$IPT4" "$IPT6"; do + [ "$IPT" = "$IPT6" ] && [ $NO_IPV6 -ne 0 ] && continue + $IPT -D PREROUTING -j mwan3_hook &> /dev/null + $IPT -D OUTPUT -j mwan3_hook &> /dev/null + + for table in $($IPT -S | awk '{print $2}' | grep mwan3 | sort -u); do + $IPT -F $table &> /dev/null + done + + for table in $($IPT -S | awk '{print $2}' | grep mwan3 | sort -u); do + $IPT -X $table &> /dev/null + done + 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 + + mwan3_unlock "command" "mwan3" + + rm -rf $MWAN3_STATUS_DIR $MWAN3TRACK_STATUS_DIR } restart() { - /etc/init.d/mwan3 enable - /etc/init.d/mwan3 stop - /etc/init.d/mwan3 start -} - -use() { - # 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 - - interface=$1 ; shift - [ -z "$*" ] && echo "no command specified for mwan3 use" && 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 $* - + stop + start } case "$1" in - ifup|ifdown|interfaces|policies|connected|rules|status|start|stop|restart|use|internal) + ifup|ifdown|interfaces|policies|connected|rules|status|start|stop|restart) mwan3_init - # shellcheck disable=SC2048 $* ;; *) diff --git a/mwan3/files/usr/sbin/mwan3rtmon b/mwan3/files/usr/sbin/mwan3rtmon index 06be43597..9165de554 100755 --- a/mwan3/files/usr/sbin/mwan3rtmon +++ b/mwan3/files/usr/sbin/mwan3rtmon @@ -1,173 +1,38 @@ #!/bin/sh . /lib/functions.sh -. /lib/functions/network.sh . /lib/mwan3/mwan3.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" +LOG="logger -t $(basename "$0")[$$] -p" + +clean_up() { + $LOG notice "Stopping mwan3rtmon..." + exit 0 +} + +rtchange() { + $LOG info "Detect rtchange event." +} + +main() { + local rtmon_interval + trap clean_up TERM + trap rtchange USR1 + + config_load mwan3 + config_get rtmon_interval globals rtmon_interval '5' + + sleep 3 + while true; do + mwan3_lock "service" "mwan3rtmon" + mwan3_rtmon_ipv4 || mwan3_rtmon_ipv6 + ret=$? + mwan3_unlock "service" "mwan3rtmon" + [ "$ret" = "0" ] || break + [ "$rtmon_interval" = "0" ] && break + sleep "$rtmon_interval" & + wait 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 error - 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 - error=$($IP route add table $tid $route_line 2>&1) || - LOG warn "failed to add $route_line to table $tid - error: $error" - } - - 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() -{ - local action route_line family tbl device line tid - - route_line=${1##"Deleted "} - route_family=$2 - - if [ "$route_line" = "$1" ]; then - action="replace" - $IPS -! add mwan3_connected_${route_family##ip} ${route_line%% *} - else - action="del" - mwan3_set_connected_${route_family} - fi - - if [ -z "${route_line##*linkdown*}" ]; then - LOG debug "attempting to add link on down interface - $route_line" - fi - - if [ "$route_family" = "ipv4" ]; then - IP="$IP4" - elif [ "$route_family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then - IP="$IP6" - else - LOG warn "route update called with invalid family - $route_family" - return - fi - route_line=$(echo "$route_line" | sed -ne "$MWAN3_ROUTE_LINE_EXP") - - handle_route() { - local error - local iface=$1 - tbl=$($IP route list table $tid 2>/dev/null)$'\n' - - if [ -n "$iface" ] && [ "$(mwan3_get_mwan3track_status $iface)" != "active" ]; then - LOG debug "interface $iface is disabled - skipping '$route_line'"; - return - 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'" - error=$($IP route "$action" table $tid $route_line 2>&1)|| - LOG warn "failed: '$IP route $action table $tid $route_line' - error: $error" - } - handle_route_cb(){ - local iface=$1 - let tid++ - config_get family "$iface" family ipv4 - [ "$family" != "$route_family" ] && return - handle_route "$iface" - } - - mwan3_update_dev_to_table - mwan3_route_line_dev "tid" "$route_line" "$route_family" - - if [ -n "$tid" ]; then - handle_route - elif [ -n "${route_line##default*}" ] && [ -n "${route_line##fe80::/64*}" ]; then - config_foreach handle_route_cb interface - fi -} - -main() -{ - local IP family - - mwan3_init - - family=$1 - [ -z $family ] && family=ipv4 - 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" - else - IP="$IP4" - fi - sh -c "echo \$\$; exec $IP monitor route" | { - read -r monitor_pid - trap_with_arg func_trap "$monitor_pid" SIGINT SIGTERM SIGKILL - KILL -SIGSTOP $$ - while IFS='' read -r line; do - [ -z "${line##*table*}" ] && continue - LOG debug "handling route update $family '$line'" - mwan3_rtmon_route_handle "$line" "$family" - done - } & - child=$! - trap_with_arg func_trap "$child" SIGINT SIGTERM SIGKILL - mwan3_set_connected_${family} - mwan3_add_all_routes ${family} - kill -SIGCONT $child - wait $child -} main "$@" diff --git a/mwan3/files/usr/sbin/mwan3track b/mwan3/files/usr/sbin/mwan3track index 2c535d59f..e112475ce 100755 --- a/mwan3/files/usr/sbin/mwan3track +++ b/mwan3/files/usr/sbin/mwan3track @@ -1,339 +1,229 @@ #!/bin/sh . /lib/functions.sh -. /lib/functions/network.sh . /lib/mwan3/common.sh +LOG="logger -t $(basename "$0")[$$] -p" INTERFACE="" DEVICE="" +PING="/bin/ping" IFDOWN_EVENT=0 -IFUP_EVENT=0 - -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() { - LOG notice "Stopping mwan3track for interface \"${INTERFACE}\". Status was \"${STATUS}\"" - stop_subprocs + $LOG notice "Stopping mwan3track for interface \"${INTERFACE}\"" exit 0 } if_down() { - LOG info "Detect ifdown event on interface ${INTERFACE} (${DEVICE})" + $LOG info "Detect ifdown event on interface ${INTERFACE} (${DEVICE})" IFDOWN_EVENT=1 - stop_subprocs -} - -if_up() { - LOG info "Detect ifup event on interface ${INTERFACE} (${DEVICE})" - IFDOWN_EVENT=0 - IFUP_EVENT=1 - STARTED=1 - stop_subprocs -} - -ping_test_host() { - if [ "$FAMILY" = "ipv6" ]; then - echo "::1" - else - echo "127.0.0.1" - fi -} - -get_ping_command() { - if [ -x "/usr/bin/ping" ] && /usr/bin/ping -${FAMILY#ipv} -c1 -q $(ping_test_host) &>/dev/null; then - # -4 option added in iputils c3e68ac6 so need to check if we can use it - # or if we must use ping and ping6 - echo "/usr/bin/ping -${FAMILY#ipv}" - elif [ "$FAMILY" = "ipv6" ] && [ -x "/usr/bin/ping6" ]; then - echo "/usr/bin/ping6" - elif [ "$FAMILY" = "ipv4" ] && [ -x "/usr/bin/ping" ]; then - echo "/usr/bin/ping" - elif [ -x "/bin/ping" ]; then - echo "/bin/ping -${FAMILY#ipv}" - else - return 1 - fi } validate_track_method() { case "$1" in ping) - PING=$(get_ping_command) - if [ $? -ne 0 ]; then - LOG warn "Missing ping. Please enable BUSYBOX_DEFAULT_PING and recompile busybox or install iputils-ping package." + which ping 1>/dev/null 2>&1 || { + $LOG warn "Missing ping. Please install iputils-ping package or enable ping util and recompile busybox." return 1 - fi + } ;; arping) - command -v arping 1>/dev/null 2>&1 || { - LOG warn "Missing arping. Please install iputils-arping package." + which arping 1>/dev/null 2>&1 || { + $LOG warn "Missing arping. Please install iputils-arping package." return 1 } ;; httping) - command -v httping 1>/dev/null 2>&1 || { - LOG warn "Missing httping. Please install httping package." + which httping 1>/dev/null 2>&1 || { + $LOG warn "Missing httping. Please install httping package." + 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-*) - command -v nping 1>/dev/null 2>&1 || { - LOG warn "Missing nping. Please install nping package." + which nping 1>/dev/null 2>&1 || { + $LOG warn "Missing nping. Please install nping package." return 1 } ;; *) - LOG warn "Unsupported tracking method: $track_method" + $LOG warn "Unsupported tracking method: $track_method" return 2 ;; 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() { - STATUS='offline' - echo "offline" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS - get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE - echo "0" > $MWAN3TRACK_STATUS_DIR/$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() { - STATUS='online' - echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS - echo "0" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE - get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/ONLINE - host_up_count=0 - lost=0 - turn=0 - loss=0 - LOG notice "Interface $INTERFACE ($DEVICE) is online" - env -i FIRSTCONNECT=$1 ACTION="connected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface -} - -disconnecting() { - if [ "$STATUS" != "disconnecting" ] ; then - STATUS="disconnecting" - echo "disconnecting" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS - LOG notice "Interface $INTERFACE ($DEVICE) is disconnecting" - env -i ACTION="disconnecting" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface - fi -} - -connecting() { - if [ "$STATUS" != "connecting" ] ; then - STATUS="connecting" - echo "connecting" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS - LOG notice "Interface $INTERFACE ($DEVICE) is connecting" - env -i ACTION="connecting" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface - fi -} - -disabled() { - STATUS='disabled' - echo "disabled" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS - STARTED=0 -} - -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 - disconnected 1 - else - connected 1 - fi -} - -update_status() { - local track_ip=$1 - - echo "$2" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip} - [ -z "$3" ] && return - echo "$3" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LATENCY_${track_ip} - echo "$4" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOSS_${track_ip} -} - main() { local reliability count timeout interval failure_interval local recovery_interval down up size local keep_failure_interval check_quality failure_latency local recovery_latency failure_loss recovery_loss + local max_ttl httping_ssl - local max_ttl httping_ssl track_ips do_log + [ -z "$5" ] && echo "Error: should not be started manually" && exit 0 INTERFACE=$1 - STATUS="" - STARTED=0 - TRACK_OUTPUT=$MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_OUTPUT - - mwan3_init - - mkdir -p $MWAN3TRACK_STATUS_DIR/$INTERFACE - + DEVICE=$2 + STATUS=$3 + SRC_IP=$4 + mkdir -p /var/run/mwan3track/$1 trap clean_up TERM trap if_down USR1 - trap if_up USR2 - config_get FAMILY $INTERFACE family ipv4 - config_get track_method $INTERFACE track_method ping - config_get_bool httping_ssl $INTERFACE httping_ssl 0 - validate_track_method $track_method || { + config_load mwan3 + config_get track_method $1 track_method ping + config_get_bool httping_ssl $1 httping_ssl 0 + validate_track_method $track_method $SRC_IP || { track_method=ping 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 - LOG err "No track method avaliable" + $LOG err "No track method avaliable" exit 1 fi } - config_get reliability $INTERFACE reliability 1 - config_get count $INTERFACE count 1 - config_get timeout $INTERFACE timeout 4 - config_get interval $INTERFACE interval 10 - config_get down $INTERFACE down 5 - config_get up $INTERFACE up 5 - config_get size $INTERFACE size 56 - config_get max_ttl $INTERFACE max_ttl 60 - config_get failure_interval $INTERFACE failure_interval $interval - config_get_bool keep_failure_interval $INTERFACE keep_failure_interval 0 - config_get recovery_interval $INTERFACE recovery_interval $interval - config_get_bool check_quality $INTERFACE check_quality 0 - config_get failure_latency $INTERFACE failure_latency 1000 - config_get recovery_latency $INTERFACE recovery_latency 500 - config_get failure_loss $INTERFACE failure_loss 40 - 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 + config_get reliability $1 reliability 1 + config_get count $1 count 1 + config_get timeout $1 timeout 4 + config_get interval $1 interval 10 + config_get down $1 down 5 + config_get up $1 up 5 + config_get size $1 size 56 + config_get max_ttl $1 max_ttl 60 + config_get failure_interval $1 failure_interval $interval + config_get_bool keep_failure_interval $1 keep_failure_interval 0 + config_get recovery_interval $1 recovery_interval $interval + config_get_bool check_quality $1 check_quality 0 + config_get failure_latency $1 failure_latency 1000 + config_get recovery_latency $1 recovery_latency 500 + config_get failure_loss $1 failure_loss 40 + config_get recovery_loss $1 recovery_loss 10 - local score=$((down+up)) + local score=$(($down+$up)) + local track_ips=$(echo $* | cut -d ' ' -f 5-99) local host_up_count=0 local lost=0 + local sleep_time=0 local turn=0 + local result + local ping_protocol=4 + local ping_result + local ping_result_raw + local ping_status + local loss=0 + local latency=0 - firstconnect + if [ "$STATUS" = "offline" ]; then + 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 - [ $STARTED -eq 0 ] && { sleep $MAX_SLEEP & SLEEP_PID=$!; wait; } - unset SLEEP_PID + sleep_time=$interval + for track_ip in $track_ips; do if [ $host_up_count -lt $reliability ]; then case "$track_method" in 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 echo $track_ip | grep -q ':'; 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 + fi if [ $check_quality -eq 0 ]; then - WRAP $PING -n -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null & - TRACK_PID=$! - wait $TRACK_PID + $PING -$ping_protocol -I ${ADDR:-$DEVICE} -c $count -W $timeout -s $size -t $max_ttl -q $track_ip &> /dev/null result=$? else - WRAP $PING -n -c $count -W $timeout -s $size -t $max_ttl -q $track_ip 2>/dev/null > $TRACK_OUTPUT & - TRACK_PID=$! - wait $TRACK_PID + 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_status=$? - loss="$(sed $TRACK_OUTPUT -ne 's/.*\([0-9]\+\)% packet loss.*/\1/p')" + ping_result=$(echo "$ping_result_raw" | tail -n2) + 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 latency=999999 loss=100 else - latency="$(sed $TRACK_OUTPUT -ne 's%\(rtt\|round-trip\).* = [^/]*/\([0-9]\+\).*%\2%p')" + latency="$(echo "$ping_result" | grep -E 'rtt|round-trip' | cut -d "=" -f2 | cut -d "/" -f2 | cut -d "." -f1)" fi fi ;; arping) - WRAP arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null & - TRACK_PID=$! - wait $TRACK_PID + arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null result=$? ;; httping) if [ "$httping_ssl" -eq 1 ]; then - WRAP httping -c $count -t $timeout -q "https://$track_ip" &> /dev/null & + httping -y $SRC_IP -c $count -t $timeout -q "https://$track_ip" &> /dev/null else - WRAP httping -c $count -t $timeout -q "http://$track_ip" &> /dev/null & + httping -y $SRC_IP -c $count -t $timeout -q "http://$track_ip" &> /dev/null fi - TRACK_PID=$! - wait $TRACK_PID result=$? ;; - nping-*) - WRAP nping -c $count $track_ip --${FAMILY#nping-} > $TRACK_OUTPUT & - TRACK_PID=$! - wait $TRACK_PID - result=$(grep $TRACK_OUTPUT Lost | awk '{print $12}') + nping-tcp) + result=$(nping -e $DEVICE -c $count $track_ip --tcp | grep Lost | awk '{print $12}') + ;; + nping-udp) + result=$(nping -e $DEVICE -c $count $track_ip --udp | grep 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 - do_log="" if [ $check_quality -eq 0 ]; then if [ $result -eq 0 ]; then let host_up_count++ - update_status "$track_ip" "up" - - [ $score -le $up ] && do_log="success" + echo "up" > /var/run/mwan3track/$1/TRACK_${track_ip} + if [ $score -le $up ]; then + $LOG info "Check ($track_method) success for target \"$track_ip\" on interface $1 ($2)" + fi else let lost++ - update_status "$track_ip" "down" - - [ $score -gt $up ] && do_log="failed" + echo "down" > /var/run/mwan3track/$1/TRACK_${track_ip} + if [ $score -gt $up ]; then + $LOG info "Check ($track_method) failed for target \"$track_ip\" on interface $1 ($2)" + fi fi - [ -n "$do_log" ] && LOG info "Check ($track_method) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score" - else - if [ "$loss" -ge "$failure_loss" ] || [ "$latency" -ge "$failure_latency" ]; then + if [ "$loss" -ge "$failure_loss" -o "$latency" -ge "$failure_latency" ]; then let lost++ - update_status "$track_ip" "down" $latency $loss + echo "down" > /var/run/mwan3track/$1/TRACK_${track_ip} + echo "$latency" > /var/run/mwan3track/$1/LATENCY_${track_ip} + echo "$loss" > /var/run/mwan3track/$1/LOSS_${track_ip} - [ $score -gt $up ] && do_log="failed" - elif [ "$loss" -le "$recovery_loss" ] && [ "$latency" -le "$recovery_latency" ]; 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)" + fi + elif [ "$loss" -le "$recovery_loss" -a "$latency" -le "$recovery_latency" ]; then let host_up_count++ - update_status "$track_ip" "up" $latency $loss + echo "up" > /var/run/mwan3track/$1/TRACK_${track_ip} + echo "$latency" > /var/run/mwan3track/$1/LATENCY_${track_ip} + echo "$loss" > /var/run/mwan3track/$1/LOSS_${track_ip} - [ $score -le $up ] && do_log="success" + if [ $score -le $up ]; then + $LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) success for target \"$track_ip\" on interface $1 ($2)" + fi else - echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip} + echo "skipped" > /var/run/mwan3track/$1/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 else - echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip} + echo "skipped" > /var/run/mwan3track/$1/TRACK_${track_ip} fi done @@ -342,64 +232,61 @@ main() { if [ $score -lt $up ]; then score=0 - [ ${keep_failure_interval} -eq 1 ] && sleep_time=$failure_interval + [ ${keep_failure_interval} -eq 1 ] && { + sleep_time=$failure_interval + } else - disconnecting sleep_time=$failure_interval fi if [ $score -eq $up ]; then - disconnected + echo "offline" > /var/run/mwan3track/$1/STATUS + env -i ACTION=ifdown INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface score=0 fi else - if [ $score -lt $((down+up)) ] && [ $lost -gt 0 ]; then - connecting - LOG info "Lost $((lost*count)) ping(s) on interface $INTERFACE ($DEVICE). Current score: $score" + if [ $score -lt $(($down+$up)) ] && [ $lost -gt 0 ]; then + $LOG info "Lost $(($lost*$count)) ping(s) on interface $1 ($2)" fi let score++ lost=0 if [ $score -gt $up ]; then - echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS - score=$((down+up)) + echo "online" > /var/run/mwan3track/$1/STATUS + score=$(($down+$up)) elif [ $score -le $up ]; then - connecting sleep_time=$recovery_interval fi if [ $score -eq $up ]; then - connected + $LOG notice "Interface $1 ($2) is online" + echo "online" > /var/run/mwan3track/$1/STATUS + env -i ACTION=ifup INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + exit 0 fi fi let turn++ - mkdir -p "$MWAN3TRACK_STATUS_DIR/${1}" - echo "${lost}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/LOST - echo "${score}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/SCORE - echo "${turn}" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TURN - get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TIME + mkdir -p "/var/run/mwan3track/${1}" + echo "${lost}" > /var/run/mwan3track/$1/LOST + echo "${score}" > /var/run/mwan3track/$1/SCORE + echo "${turn}" > /var/run/mwan3track/$1/TURN + echo "$(get_uptime)" > /var/run/mwan3track/$1/TIME host_up_count=0 - if [ "${IFDOWN_EVENT}" -eq 0 ] && [ "${IFUP_EVENT}" -eq 0 ]; then - sleep "${sleep_time}" & - SLEEP_PID=$! - wait - unset SLEEP_PID - fi + sleep "${sleep_time}" & + wait if [ "${IFDOWN_EVENT}" -eq 1 ]; then - LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})" - disabled - disconnected + echo "offline" > /var/run/mwan3track/$1/STATUS + echo "$(get_uptime)" > /var/run/mwan3track/$1/OFFLINE + 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 fi - if [ "${IFUP_EVENT}" -eq 1 ]; then - LOG debug "Register ifup event on interface ${INTERFACE} (${DEVICE})" - firstconnect - IFUP_EVENT=0 - fi done } diff --git a/mwan3/src/sockopt_wrap.c b/mwan3/src/sockopt_wrap.c deleted file mode 100644 index 695e57502..000000000 --- a/mwan3/src/sockopt_wrap.c +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2020 Aaron Goodman . 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 -#include -#include -#include -#include -#include -#include -#include - -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; -}