#!/bin/sh
#
# Copyright (C) 2018-2025 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
# This script test global or interface speed
#

FORCEVPS=false
FASTTEST=false
INTERFACE=""
for arg in "$@"; do
	case "$arg" in
		forcevps)
			FORCEVPS=true
			;;
		fasttest)
			FASTTEST=true
			;;
		*)
			# Keep the last non-flag argument as interface name.
			INTERFACE="$arg"
			;;
	esac
done

IPV6=false
if [ "$(basename $0)" = "omr-test-speedv6" ]; then
	IPV6=true
fi

[ -n "$INTERFACE" ] && [ ! -d "/sys/class/net/$INTERFACE" ] && {
	echo "You must use a real interface. You wan find them using 'ip a' for example (not $INTERFACE)"
	exit 1
}

_DEFAULT_HOSTLST="http://scaleway.testdebit.info/10G.iso http://appliwave.testdebit.info/10G/10G.iso http://speedtest.frankfurt.linode.com/garbage.php?ckSize=10000 http://speedtest.tokyo2.linode.com/garbage.php?ckSize=10000 http://speedtest.singapore.linode.com/garbage.php?ckSize=10000 http://speedtest.newark.linode.com/garbage.php?ckSize=10000 http://speedtest.atlanta.linode.com/garbage.php?ckSize=10000 http://speedtest.dallas.linode.com/garbage.php?ckSize=10000 http://speedtest.fremont.linode.com/garbage.php?ckSize=10000 http://par.download.datapacket.com/10000mb.bin http://nyc.download.datapacket.com/10000mb.bin http://ams.download.datapacket.com/10000mb.bin http://fra.download.datapacket.com/10000mb.bin http://lon.download.datapacket.com/10000mb.bin http://mad.download.datapacket.com/10000mb.bin http://prg.download.datapacket.com/10000mb.bin http://sto.download.datapacket.com/10000mb.bin http://vie.download.datapacket.com/10000mb.bin http://war.download.datapacket.com/10000mb.bin http://atl.download.datapacket.com/10000mb.bin http://chi.download.datapacket.com/10000mb.bin http://lax.download.datapacket.com/10000mb.bin http://mia.download.datapacket.com/10000mb.bin http://nyc.download.datapacket.com/10000mb.bin http://speedtest.milkywan.fr/files/10G.iso https://rbx.proof.ovh.net/files/10Gb.dat https://gra.proof.ovh.net/files/10Gb.dat https://sbg.proof.ovh.net/files/10Gb.dat https://bhs.proof.ovh.ca/files/10Gb.dat https://vin.proof.ovh.us/files/10Gb.dat https://hil.proof.ovh.us/files/10Gb.dat http://speedtest.tele2.net/10GB.zip https://la.speedtest.clouvider.net/10g.bin https://atl.speedtest.clouvider.net/10g.bin https://nyc.speedtest.clouvider.net/10g.bin https://lon.speedtest.clouvider.net/10g.bin https://man.speedtest.clouvider.net/10g.bin https://ams.speedtest.clouvider.net/10g.bin https://fra.speedtest.clouvider.net/10g.bin https://dal.speedtest.clouvider.net/10g.bin https://ash.speedtest.clouvider.net/10g.bin https://phx.speedtest.clouvider.net/10g.bin https://chi.speedtest.clouvider.net/10g.bin https://akl.download.datapacket.com/10000mb.bin https://syd.download.datapacket.com/10000mb.bin http://speedtest.sydney.linode.com/garbage.php?ckSize=10000"
_DEFAULT_HOSTLST6="http://scaleway.testdebit.info/10G.iso http://appliwave.testdebit.info/10G/10G.iso http://speedtest.frankfurt.linode.com/garbage.php?ckSize=10000 http://speedtest.tokyo2.linode.com/garbage.php?ckSize=10000 http://speedtest.singapore.linode.com/garbage.php?ckSize=10000 http://speedtest.newark.linode.com/garbage.php?ckSize=10000 http://speedtest.atlanta.linode.com/garbage.php?ckSize=10000 http://speedtest.dallas.linode.com/garbage.php?ckSize=10000 http://speedtest.fremont.linode.com/garbage.php?ckSize=10000 http://par.download.datapacket.com/10000mb.bin http://nyc.download.datapacket.com/10000mb.bin http://ams.download.datapacket.com/10000mb.bin http://fra.download.datapacket.com/10000mb.bin http://lon.download.datapacket.com/10000mb.bin http://mad.download.datapacket.com/10000mb.bin http://prg.download.datapacket.com/10000mb.bin http://sto.download.datapacket.com/10000mb.bin http://vie.download.datapacket.com/10000mb.bin http://war.download.datapacket.com/10000mb.bin http://atl.download.datapacket.com/10000mb.bin http://chi.download.datapacket.com/10000mb.bin http://lax.download.datapacket.com/10000mb.bin http://mia.download.datapacket.com/10000mb.bin http://nyc.download.datapacket.com/10000mb.bin http://speedtest.milkywan.fr/files/10G.iso https://rbx.proof.ovh.net/files/10Gb.dat https://gra.proof.ovh.net/files/10Gb.dat https://sbg.proof.ovh.net/files/10Gb.dat https://bhs.proof.ovh.ca/files/10Gb.dat https://vin.proof.ovh.us/files/10Gb.dat https://hil.proof.ovh.us/files/10Gb.dat http://speedtest.tele2.net/10GB.zip https://la.speedtest.clouvider.net/10g.bin https://atl.speedtest.clouvider.net/10g.bin https://nyc.speedtest.clouvider.net/10g.bin https://lon.speedtest.clouvider.net/10g.bin https://man.speedtest.clouvider.net/10g.bin https://ams.speedtest.clouvider.net/10g.bin https://fra.speedtest.clouvider.net/10g.bin https://dal.speedtest.clouvider.net/10g.bin https://ash.speedtest.clouvider.net/10g.bin https://phx.speedtest.clouvider.net/10g.bin https://chi.speedtest.clouvider.net/10g.bin https://akl.download.datapacket.com/10000mb.bin https://syd.download.datapacket.com/10000mb.bin http://speedtest.sydney.linode.com/garbage.php?ckSize=10000"

HOSTLST=$(uci -q get omr-speedtest.urls.download 2>/dev/null | tr '\n' ' ')
[ -z "$HOSTLST" ] && HOSTLST="$_DEFAULT_HOSTLST"
HOSTLST6=$(uci -q get omr-speedtest.urls.download6 2>/dev/null | tr '\n' ' ')
[ -z "$HOSTLST6" ] && HOSTLST6="$_DEFAULT_HOSTLST6"

select_server() {
	[ "$FASTTEST" = true ] || echo "Select best test server..."
	bestping="9999"
	HOST=""
	if [ "$IPV6" = true ]; then
		for pinghost in $HOSTLST6; do
			domain=$(echo $pinghost | awk -F/ '{print $3}')
			if [ -z "$INTERFACE" ] || [ "$FORCEVPS" = true ]; then
				ping=$(ping -6 -c1 -w2 $domain | cut -d "/" -s -f5 | cut -d "." -f1 | tr -d '\n')
			else
				ping=$(ping -6 -c1 -w2 -I $INTERFACE -B $domain | cut -d "/" -s -f5 | cut -d "." -f1 | tr -d '\n')
			fi
			[ "$FASTTEST" = true ] || echo "host: $domain - ping: $ping"
			if [ -n "$ping" ] && [ "$ping" -lt "$bestping" ]; then
				bestping=$ping
				HOST=$pinghost
			fi
		done
	else
		for pinghost in $HOSTLST; do
			domain=$(echo $pinghost | awk -F/ '{print $3}')
			if [ -z "$INTERFACE" ] || [ "$FORCEVPS" = true ]; then
				ping=$(ping -4 -c1 -w2 $domain | cut -d "/" -s -f5 | cut -d "." -f1 | tr -d '\n')
			else
				ping=$(ping -4 -c1 -w2 -I $INTERFACE -B $domain | cut -d "/" -s -f5 | cut -d "." -f1 | tr -d '\n')
			fi
			[ "$FASTTEST" = true ] || echo "host: $domain - ping: $ping"
			if [ -n "$ping" ] && [ "$ping" -lt "$bestping" ]; then
				bestping=$ping
				HOST=$pinghost
			fi
		done
	fi
}

get_speed_global() {
	HOST=$1
	if [ "$IPV6" = true ]; then
		curl -6 -o /dev/null $HOST || echo
	else
		curl -4 -o /dev/null $HOST || echo
	fi
}

get_speed_global_fast() {
	HOST=$1
	if [ "$IPV6" = true ]; then
		avg_speed=$(curl -6 --max-time 10 -o /dev/null -qfsS -w '%{speed_download}' $HOST 2>/dev/null)
	else
		avg_speed=$(curl -4 --max-time 10 -o /dev/null -qfsS -w '%{speed_download}' $HOST 2>/dev/null)
	fi
	echo "$avg_speed"
}

# The router's own connections to the test host must not go through the
# transparent proxy: its output chain (ss_rules_local_out, or the xr_/v2r_
# one) lets through what is in the proxy's dst_bypass set, and omr-bypass's
# omr_dst_bypass_all_4/6 when it has one. The ipsets used before don't exist
# with fw4. Only what was added is removed afterwards: an address the user
# already bypasses stays.
_proxy_bypass_added=""
_proxy_bypass_add() {
	local fam="$1" ip set
	shift
	for ip in "$@"; do
		case "$ip" in ""|*[!0-9a-fA-F.:]*) continue ;; esac
		for set in ss_rules${fam}_dst_bypass v2r_rules${fam}_dst_bypass xr_rules${fam}_dst_bypass omr_dst_bypass_all_${fam:-4}; do
			nft get element inet fw4 "$set" "{ $ip }" >/dev/null 2>&1 && continue
			nft add element inet fw4 "$set" "{ $ip }" >/dev/null 2>&1 && _proxy_bypass_added="$_proxy_bypass_added $set,$ip"
		done
	done
}
_proxy_bypass_del() {
	local e
	for e in $_proxy_bypass_added; do
		nft delete element inet fw4 "${e%%,*}" "{ ${e#*,} }" >/dev/null 2>&1
	done
	_proxy_bypass_added=""
}

bypass_host_enable() {
	INTERFACE=$1
	HOST=$2
	SQM_STOPPED=false
	if tc qdisc show dev "$INTERFACE" | grep -q ingress; then
		/etc/init.d/sqm stop "$INTERFACE"
		SQM_STOPPED=true
	fi
	domain=$(echo $HOST | awk -F/ '{print $3}')
	if [ "$IPV6" = true ]; then
		hostip=$(dig +nocmd +noall +answer AAAA $domain | grep -v CNAME | awk '{print $5}' | tr '\n' ' ')
		_proxy_bypass_add 6 $hostip
	else
		hostip=$(dig +nocmd +noall +answer A $domain | grep -v CNAME | awk '{print $5}' | tr '\n' ' ')
		_proxy_bypass_add "" $hostip
	fi
}


bypass_host_disable() {
	INTERFACE=$1
	HOST=$2
	_proxy_bypass_del
	[ "$SQM_STOPPED" = true ] && /etc/init.d/sqm start "$INTERFACE"
}

get_speed_interface() {
	INTERFACE=$1
	HOST=$2
	bypass_host_enable $INTERFACE $HOST
	if [ "$IPV6" = true ]; then
		curl -6 -o /dev/null --interface $INTERFACE $HOST || echo
	else
		curl -4 -o /dev/null --interface $INTERFACE $HOST || echo
	fi
	bypass_host_disable $INTERFACE $HOST
}

get_speed_interface_fast() {
	INTERFACE=$1
	HOST=$2
	bypass_host_enable $INTERFACE $HOST
	if [ "$IPV6" = true ]; then
		avg_speed=$(curl -6 --max-time 10 -o /dev/null -qfsS -w '%{speed_download}' --interface $INTERFACE $HOST 2>/dev/null)
	else
		avg_speed=$(curl -4 --max-time 10 -o /dev/null -qfsS -w '%{speed_download}' --interface $INTERFACE $HOST 2>/dev/null)
	fi
	echo "$avg_speed"
	bypass_host_disable $INTERFACE $HOST
}

response="000"
try=0
while [ "$response" = "000" ] && [ "$try" -le 3 ]; do
	select_server
	if [ "$IPV6" = true ]; then
		response=$(curl -6 --write-out '%{http_code}' --silent --head --insecure -IL  --connect-timeout 5 --output /dev/null $HOST)
		[ -n "$HOST" ] && HOSTLST6=$(echo $HOSTLST6 | tr ' ' '\n' | grep -v "$HOST" | xargs)
	else
		response=$(curl -4 --write-out '%{http_code}' --silent --head --insecure -IL  --connect-timeout 5 --output /dev/null $HOST)
		[ -n "$HOST" ] && HOSTLST=$(echo $HOSTLST | tr ' ' '\n' | grep -v "$HOST" | xargs)
	fi
	try=$((try+1))
	if [ "$response" = "000" ] && [ "$FASTTEST" != true ]; then
		echo "No answer from $HOST, retry..."
	fi
done

[ -z "$HOST" ] && HOST="http://speedtest.milkywan.fr/files/10G.iso"

[ "$FASTTEST" = true ] || echo "Best server is $HOST, running test:"
trap : HUP INT TERM
if [ -z "$INTERFACE" ]; then
	if [ "$FASTTEST" = true ]; then 
		get_speed_global_fast $HOST
	else
		get_speed_global $HOST
	fi
else
	if [ "$FASTTEST" = true ]; then 
		get_speed_interface_fast $INTERFACE $HOST
	else
		get_speed_interface $INTERFACE $HOST
	fi
fi
