#!/bin/sh
# shellcheck disable=SC1091,SC1090
# vim: set noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 :

[ -n "$1" ] || exit

. /lib/functions.sh

# retrieve args
OMR_TRACKER_INTERFACE="$1"
shift

# export vars
export OMR_TRACKER_INTERFACE
export OMR_TRACKER_HOST
export OMR_TRACKER_TIMEOUT
export OMR_TRACKER_STATUS
export OMR_TRACKER_STATUS_MSG
export OMR_TRACKER_PREV_STATUS
export OMR_TRACKER_DEVICE
export OMR_TRACKER_DEVICE_IP
export OMR_TRACKER_DEVICE_GATEWAY

dscp=56   # set DSCP CS7 (56) in outgoing packets
initial_hosts="$OMR_TRACKER_HOSTS"
initial_timeout="$OMR_TRACKER_TIMEOUT"

# set constants for rto updating
# we've changed the default values of the RFC to K=3 and beta=0.25 instead of K=4 and beta=0.125
# this allows us to lower the gap between rtt and rto when the latency increases quickly as our
# measurements are less frequent than the standard timeout computation of the TCP stack
_init_rto() {
	srtt=
	rttvar=
	rto_init=$(( initial_timeout * 1000 ))
	rto=$rto_init
}

# update rto as explained in rfc6298
# rto    : retransmission timeout.
# rtt    : round trip time
# rttvar : round trip time variance
# srtt   : smoothed round trip time
# alpha  : smoothing coeff for srtt
# beta   : smoothing coeff for rttvar
# K      : coeff to increase rttvar impact on rto computation
_update_rto() {
	if [ -z "$srtt" ]; then
		srtt=$1
		rttvar=$(($1 / 2))
	else
		diff=$((srtt - $1))
		rttvar=$(((75 * rttvar + 25 * (diff >= 0 ? diff : -diff)) / 100))
		srtt=$(((75 * srtt + 25 * $1) / 100))
	fi
	rto=$((tmp = srtt + 3 * rttvar, tmp > rto_init ? tmp : rto_init))
}

_restart() {
	OMR_TRACKER_HOST="${OMR_TRACKER_HOSTS%% *}"
	[ "$OMR_TRACKER_HOST" = "$OMR_TRACKER_HOSTS" ] || {
		OMR_TRACKER_HOSTS="${OMR_TRACKER_HOSTS#* } $OMR_TRACKER_HOST"
		_init_rto
	}
}

_post_tracking() {
	for tracker_bin in /usr/share/omr/post-tracking.d/*; do
		[ -x "$tracker_bin" ] && (
			_log() {
				logger -t "post-tracking-${tracker_bin##*/}" "$*"
			}
			. "$tracker_bin"
		)
	done
}

_ping_server() {
	local serverip=$(uci -q get openmptcprouter.$1.ip)
	local device=$2
	if [ -n "$serverip" ] && [ -n "$device" ]; then
		_ping $serverip $device "yes"
		statusp=$?
		if $(exit $statusp); then
			serverip_ping=true
		fi
	else
		serverip_ping=false
	fi
}

_ping() {
	local host=$1
	local device=$2
	local localip=$3
	if [ "$(uci -q get network.$OMR_TRACKER_INTERFACE.proto)" = "3g" ] || [ "$(uci -q get network.$OMR_TRACKER_INTERFACE.proto)" = "qmi" ] || [ "$(uci -q get network.$OMR_TRACKER_INTERFACE.proto)" = "ncm" ]; then
		ret=$(ping -I "${device}" \
			-w "$OMR_TRACKER_TIMEOUT" \
			-c 2 \
			-Q 184 \
			"${host}"
		) && echo "$ret" | grep -sq "bytes from" && {
			if [ "$localip" = "yes" ]; then
				OMR_TRACKER_LATENCY=$(echo "$ret" | cut -d "/" -s -f5 | cut -d "." -f1)
				_update_rto "$OMR_TRACKER_LATENCY"
			fi
			return
		}
	else
		ret=$(ping -B -I "${device}" \
			-w "$OMR_TRACKER_TIMEOUT" \
			-c 2 \
			-Q 184 \
			"${host}"
		) && echo "$ret" | grep -sq "bytes from" && {
			if [ "$localip" = "yes" ]; then
				OMR_TRACKER_LATENCY=$(echo "$ret" | cut -d "/" -s -f5 | cut -d "." -f1)
				_update_rto "$OMR_TRACKER_LATENCY"
			fi
			return
		}
	fi
	false
}

_httping() {
	local host=$1
	local deviceip=$2
	local localip=$3
	ret=$(httping "${host}" \
		-y "${deviceip}" \
		-t "$OMR_TRACKER_TIMEOUT" \
		-c 1 \
		-q
	) && echo "$ret" | grep -sq "1 ok" && {
		if [ "$localip" = "yes" ]; then
			OMR_TRACKER_LATENCY=$(echo "$ret" | cut -d "/" -s -f5 | cut -d "." -f1)
			_update_rto "$OMR_TRACKER_LATENCY"
		fi
		return
	}
	false
}

_dns() {
	local host=$1
	local deviceip=$2
	ret=$(dig @"${host}" \
		-b "${deviceip}" \
		    +time="$OMR_TRACKER_TIMEOUT" \
		    +tries=1 \
		    openmptcprouter.com
	) && echo "$ret" | grep -sq "94.23.252.192" && {
		OMR_TRACKER_LATENCY=$(echo "$ret" | awk '/Query time/{print $4}')
		_update_rto "$OMR_TRACKER_LATENCY"
		return
	}
	false
}

_none() {
	return
}

_restart

OMR_TRACKER_PREV_STATUS=""
# main loop
while true; do
	# setup tracker variables
	OMR_TRACKER_DEVICE_IP=
	OMR_TRACKER_STATUS="ERROR"
	OMR_TRACKER_STATUS_MSG=""
	OMR_TRACKER_LATENCY=
	#OMR_TRACKER_TIMEOUT=$((rto / 1000 + (rto % 1000 ? 1 : 0)))
	OMR_TRACKER_LIST_HOSTS=""
	OMR_TRACKER_DEVICE_GATEWAY=
	serverip_ping=false

	if [ -d "/sys/class/net/$OMR_TRACKER_DEVICE" ]; then
		if [ -n "$(ip link show $OMR_TRACKER_DEVICE | grep UP)" ]; then
			# retrieve iface ip and gateway
			OMR_TRACKER_DEVICE_IP=$(ip -4 -br addr ls dev "$OMR_TRACKER_DEVICE" | awk -F'[ /]+' '{print $3}')
			if [ -z "$OMR_TRACKER_DEVICE_IP" ]; then
				OMR_TRACKER_DEVICE_IP=$(ip -4 addr show dev "$OMR_TRACKER_DEVICE" | grep -m 1 inet | awk '{print $2}' | cut -d'/' -s -f1)
			fi
			#OMR_TRACKER_DEVICE_IP=$(ubus call network.interface.$OMR_TRACKER_INTERFACE status | jsonfilter -e '@["ipv4-address"][0].address' | tr -d "\n")
			#if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ]; then
			#	OMR_TRACKER_DEVICE_GATEWAY=$(ip -4 r list dev "$OMR_TRACKER_DEVICE" | grep -v default | awk '/proto static/ {print $1}' | tr -d "\n")
			#fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ]; then
				OMR_TRACKER_DEVICE_GATEWAY=$(uci -q get "network.$OMR_TRACKER_INTERFACE.gateway")
			fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ] || [ "$OMR_TRACKER_DEVICE_GATEWAY" = "0.0.0.0" ]; then
				OMR_TRACKER_DEVICE_GATEWAY=$(ubus call network.interface.$OMR_TRACKER_INTERFACE status | jsonfilter -q -l 1 -e '@.inactive.route[@.target="0.0.0.0"].nexthop' | tr -d "\n")
			fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ] || [ "$OMR_TRACKER_DEVICE_GATEWAY" = "0.0.0.0" ]; then
				OMR_TRACKER_DEVICE_GATEWAY=$(ubus call network.interface.$OMR_TRACKER_INTERFACE status | jsonfilter -q -l 1 -e '@.route[@.target="0.0.0.0"].nexthop' | tr -d "\n")
			fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ] || [ "$OMR_TRACKER_DEVICE_GATEWAY" = "0.0.0.0" ]; then
				OMR_TRACKER_DEVICE_GATEWAY=$(ubus call network.interface.${OMR_TRACKER_INTERFACE}_4 status 2>/dev/null | jsonfilter -q -l 1 -e '@.inactive.route[@.target="0.0.0.0"].nexthop' | tr -d "\n")
			fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ] || [ "$OMR_TRACKER_DEVICE_GATEWAY" = "0.0.0.0" ]; then
				if [ "$OMR_TRACKER_INTERFACE" = "omrvpn" ] && [ "$(uci -q get glorytun.vpn.enable)" = "1" ]; then
					OMR_TRACKER_DEVICE_GATEWAY=$(uci -q get glorytun.vpn.remoteip)
				else
					OMR_TRACKER_DEVICE_GATEWAY=""
				fi
			fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ]; then
				OMR_TRACKER_DEVICE_ROUTE=$(ip -4 r list dev "$OMR_TRACKER_DEVICE" | grep via | grep -v default | grep -v metric | grep -v / | awk '{print $1; exit}' | tr -d "\n")
			fi
			if [ -z "$OMR_TRACKER_DEVICE_GATEWAY" ]; then
				OMR_TRACKER_DEVICE_GATEWAY=$(ip -4 r list dev "$OMR_TRACKER_DEVICE" | grep kernel | awk '/proto kernel/ {print $1}' | tr -d "\n")
			fi

			# execute specific tracker
			if [ -n "$OMR_TRACKER_DEVICE_IP" ] && [ -n "$OMR_TRACKER_DEVICE_GATEWAY" ]; then
				# setup loop variable
				tries="$OMR_TRACKER_TRIES"
				# loop until tries attempts have been reached
				while [ "$tries" -gt 0 ]; do
					if [ -n "$OMR_TRACKER_DEVICE_ROUTE" ]; then
						_ping "$OMR_TRACKER_DEVICE_ROUTE" "$OMR_TRACKER_DEVICE" "no"
						status=$?
					else
						_ping "$OMR_TRACKER_DEVICE_GATEWAY" "$OMR_TRACKER_DEVICE" "no"
						status=$?
					fi
					if $(exit $status) && [ "$OMR_TRACKER_TYPE" = "none" ]; then
						OMR_TRACKER_STATUS_MSG=""
						OMR_TRACKER_STATUS="OK"
						break
					elif [ "$OMR_TRACKER_TYPE" != "none" ]; then
						if ! $(exit $status); then
							OMR_TRACKER_STATUS_MSG="gateway down"
						fi
						serverip_ping=false
						if [ "$OMR_TRACKER_TYPE" = "ping" ]; then
							config_load openmptcprouter
							config_foreach _ping_server server $OMR_TRACKER_DEVICE
						fi
						if [ "$serverip_ping" = false ] && [ -n "$OMR_TRACKER_HOST" ]; then
							OMR_TRACKER_HOST=$(resolveip -4 $OMR_TRACKER_HOST | tr -d "\n")
							if [ "$(uci -q get network.$OMR_TRACKER_INTERFACE.proto)" = "3g" ] || [ "$(uci -q get network.$OMR_TRACKER_INTERFACE.proto)" = "qmi" ] || [ "$(uci -q get network.$OMR_TRACKER_INTERFACE.proto)" = "ncm" ]; then
								# Check if route is not used
								while ! ip route add $OMR_TRACKER_HOST via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE src $OMR_TRACKER_DEVICE_IP > /dev/null 2>&1
								do
									logger -t "omr-tracker" "Can't create route to $OMR_TRACKER_HOST via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE src $OMR_TRACKER_DEVICE_IP. waiting..."
									sleep 2
									ip route del "$OMR_TRACKER_HOST" via "$OMR_TRACKER_DEVICE_GATEWAY" dev "$OMR_TRACKER_DEVICE" src "$OMR_TRACKER_DEVICE_IP" > /dev/null 2>&1
									_restart
								done
							fi
							if [ "$OMR_TRACKER_TYPE" = "ping" ]; then
								_ping "$OMR_TRACKER_HOST" "$OMR_TRACKER_DEVICE" "yes"
								statusb=$?
							elif [ "$OMR_TRACKER_TYPE" = "httping" ]; then
								_httping "$OMR_TRACKER_HOST" "$OMR_TRACKER_DEVICE_IP" "yes"
								statusb=$?
							elif [ "$OMR_TRACKER_TYPE" = "dns" ]; then
								_dns "$OMR_TRACKER_HOST" "$OMR_TRACKER_DEVICE_IP" "yes"
								statusb=$?
							fi
							ip route del "$OMR_TRACKER_HOST" via "$OMR_TRACKER_DEVICE_GATEWAY" dev "$OMR_TRACKER_DEVICE" src "$OMR_TRACKER_DEVICE_IP" > /dev/null 2>&1
							if $(exit $statusb); then
								OMR_TRACKER_STATUS_MSG=""
								OMR_TRACKER_STATUS="OK"
								break
							else
								if [ "$OMR_TRACKER_LIST_HOSTS" = "" ]; then
									OMR_TRACKER_LIST_HOSTS="$OMR_TRACKER_HOST"
								else
									OMR_TRACKER_LIST_HOSTS="$OMR_TRACKER_LIST_HOSTS,$OMR_TRACKER_HOST"
								fi
							fi
						else
							OMR_TRACKER_STATUS_MSG=""
							OMR_TRACKER_STATUS="OK"
							break
						fi
					elif ! $(exit $status); then
						OMR_TRACKER_STATUS_MSG="gateway down"
					fi
					tries=$((tries - 1))
					#_restart
					OMR_TRACKER_HOST="${OMR_TRACKER_HOSTS%% *}"
					[ "$OMR_TRACKER_HOST" = "$OMR_TRACKER_HOSTS" ] || {
						OMR_TRACKER_HOSTS="${OMR_TRACKER_HOSTS#* } $OMR_TRACKER_HOST"
					}
					#OMR_TRACKER_TIMEOUT=$((OMR_TRACKER_TIMEOUT * 2))
					sleep "$OMR_TRACKER_INTERVAL_TRIES"
				done
			fi
		else
			OMR_TRACKER_STATUS_MSG="link down"
		fi
	fi

	if [ "$OMR_TRACKER_LIST_HOSTS" != "" ]; then
		if [ "$OMR_TRACKER_STATUS_MSG" = "" ]; then
			OMR_TRACKER_STATUS_MSG="$OMR_TRACKER_TYPE from $OMR_TRACKER_DEVICE_IP error ($OMR_TRACKER_LIST_HOSTS)"
		else
			OMR_TRACKER_STATUS_MSG="$OMR_TRACKER_STATUS_MSG and $OMR_TRACKER_TYPE from $OMR_TRACKER_DEVICE_IP error ($OMR_TRACKER_LIST_HOSTS)"
		fi
	fi

	#[ "$OMR_TRACKER_HOSTS" = "$initial_hosts" ] || [ "$OMR_TRACKER_STATUS" = "OK" ] && _post_tracking
	#[ "$OMR_TRACKER_STATUS" = "ERROR" ] && _restart
	#[ "$OMR_TRACKER_STATUS" != "$OMR_TRACKER_PREV_STATUS" ] && _post_tracking
	_post_tracking
	OMR_TRACKER_PREV_STATUS="$OMR_TRACKER_STATUS"
	_restart

	sleep "$OMR_TRACKER_INTERVAL"
done
