| #!/bin/sh |
| |
| . /lib/functions.sh |
| . /lib/functions/network.sh |
| . /lib/mwan3/common.sh |
| |
| INTERFACE="" |
| DEVICE="" |
| |
| 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 |
| exit 0 |
| } |
| |
| if_down() { |
| 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." |
| return 1 |
| fi |
| ;; |
| arping) |
| command -v 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." |
| return 1 |
| } |
| ;; |
| nping-*) |
| command -v nping 1>/dev/null 2>&1 || { |
| LOG warn "Missing nping. Please install nping package." |
| return 1 |
| } |
| ;; |
| *) |
| 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() { |
| local status="$(cat ${MWAN3TRACK_STATUS_DIR}/${INTERFACE}/STATUS)" |
| |
| 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 |
| |
| # Only execute disconnectd action if status was online or disconnecting |
| if [ "$status" = "online" ] || [ "$status" = "disconnecting" ]; then |
| LOG notice "Interface $INTERFACE ($DEVICE) is offline" |
| env -i ACTION="disconnected" INTERFACE="$INTERFACE" DEVICE="$DEVICE" /sbin/hotplug-call iface |
| else |
| LOG notice "Skip disconnected event for $INTERFACE ($DEVICE)" |
| fi |
| } |
| |
| connected() { |
| STATUS='online' |
| echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS |
| echo "0" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/OFFLINE |
| get_uptime > $MWAN3TRACK_STATUS_DIR/$INTERFACE/ONLINE |
| score=$((down+up)) |
| 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 track_ips do_log |
| |
| INTERFACE=$1 |
| STATUS="" |
| STARTED=0 |
| TRACK_OUTPUT=$MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_OUTPUT |
| |
| mwan3_init |
| |
| mkdir -p $MWAN3TRACK_STATUS_DIR/$INTERFACE |
| |
| 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 || { |
| track_method=ping |
| if validate_track_method $track_method; then |
| LOG warn "Using ping to track interface $INTERFACE avaliability" |
| else |
| 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 |
| |
| local score=$((down+up)) |
| local host_up_count=0 |
| local lost=0 |
| local turn=0 |
| |
| firstconnect |
| 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) |
| 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 |
| 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_status=$? |
| loss="$(sed $TRACK_OUTPUT -ne 's/.* \([0-9]\+\)% packet loss.*/\1/p')" |
| 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')" |
| fi |
| fi |
| ;; |
| arping) |
| WRAP arping -I $DEVICE -c $count -w $timeout -q $track_ip &> /dev/null & |
| TRACK_PID=$! |
| wait $TRACK_PID |
| result=$? |
| ;; |
| httping) |
| if [ $check_quality -eq 0 ]; then |
| if [ "$httping_ssl" -eq 1 ]; then |
| WRAP httping -c $count -t $timeout -q "https://$track_ip" &> /dev/null & |
| else |
| WRAP httping -c $count -t $timeout -q "http://$track_ip" &> /dev/null & |
| fi |
| TRACK_PID=$! |
| wait $TRACK_PID |
| result=$? |
| else |
| if [ "$httping_ssl" -eq 1 ]; then |
| WRAP httping -c $count -t $timeout "https://$track_ip" 2> /dev/null > $TRACK_OUTPUT & |
| else |
| WRAP httping -c $count -t $timeout "http://$track_ip" 2> /dev/null > $TRACK_OUTPUT & |
| fi |
| TRACK_PID=$! |
| wait $TRACK_PID |
| ping_status=$? |
| loss="$(sed $TRACK_OUTPUT -ne 's/.* \([0-9]\+\).*% failed.*/\1/p')" |
| 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')" |
| fi |
| fi |
| ;; |
| nping-*) |
| WRAP nping -c $count $track_ip --${FAMILY#nping-} > $TRACK_OUTPUT & |
| TRACK_PID=$! |
| wait $TRACK_PID |
| result=$(grep Lost $TRACK_OUTPUT | 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" |
| else |
| let lost++ |
| update_status "$track_ip" "down" |
| |
| [ $score -gt $up ] && do_log="failed" |
| 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 |
| let lost++ |
| update_status "$track_ip" "down" $latency $loss |
| |
| [ $score -gt $up ] && do_log="failed" |
| elif [ "$loss" -le "$recovery_loss" ] && [ "$latency" -le "$recovery_latency" ]; then |
| let host_up_count++ |
| update_status "$track_ip" "up" $latency $loss |
| |
| [ $score -le $up ] && do_log="success" |
| else |
| echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip} |
| fi |
| [ -n "$do_log" ] && LOG info "Check (${track_method}: latency=${latency}ms loss=${loss}%) ${do_log} for target \"$track_ip\" on interface $INTERFACE ($DEVICE). Current score: $score" |
| fi |
| else |
| echo "skipped" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/TRACK_${track_ip} |
| fi |
| done |
| |
| if [ $host_up_count -lt $reliability ]; then |
| let score-- |
| |
| if [ $score -lt $up ]; then |
| score=0 |
| [ ${keep_failure_interval} -eq 1 ] && sleep_time=$failure_interval |
| elif [ $score -eq $up ]; then |
| disconnecting |
| sleep_time=$failure_interval |
| disconnected |
| elif [ $score -gt $up ]; then |
| disconnecting |
| sleep_time=$failure_interval |
| fi |
| else |
| if [ $score -lt $((down+up)) ] && [ $lost -gt 0 ]; then |
| LOG info "Lost $((lost*count)) ping(s) on interface $INTERFACE ($DEVICE). Current score: $score" |
| fi |
| |
| let score++ |
| lost=0 |
| |
| if [ $score -lt $up ]; then |
| connecting |
| sleep_time=$recovery_interval |
| elif [ $score -eq $up ]; then |
| connecting |
| sleep_time=$recovery_interval |
| connected |
| elif [ $score -gt $up ]; then |
| echo "online" > $MWAN3TRACK_STATUS_DIR/$INTERFACE/STATUS |
| score=$((down+up)) |
| 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 |
| |
| host_up_count=0 |
| if [ "${IFDOWN_EVENT}" -eq 0 ] && [ "${IFUP_EVENT}" -eq 0 ]; then |
| sleep "${sleep_time}" & |
| SLEEP_PID=$! |
| wait |
| unset SLEEP_PID |
| fi |
| |
| if [ "${IFDOWN_EVENT}" -eq 1 ]; then |
| LOG debug "Register ifdown event on interface ${INTERFACE} (${DEVICE})" |
| disconnected |
| disabled |
| 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 |
| } |
| |
| main "$@" |