#!/bin/sh
#
# Copyright (C) 2018-2026 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 manages glorytun-udp paths when an interface comes up

# A WAN tracked with the glorytun-udp type is only OK once its path runs, and
# nothing else adds that path: after a tunnel (re)start or a path mud dropped
# it stayed in ERROR forever if the path was only added on OK
if [ "$OMR_TRACKER_STATUS" = "ERROR" ] && [ "$OMR_TRACKER_TYPE" != "glorytun-udp" ]; then
	exit 0
fi

if [ -z "$OMR_TRACKER_INTERFACE" ]; then
	exit 0
fi

if [ "$(uci -q get glorytun-udp.vpn.enable)" != "1" ]; then
	exit 0
fi

gtudp_init="/etc/init.d/glorytun-udp"
[ -x "$gtudp_init" ] || gtudp_init="glorytun-udp"

# Resume a hold-down: 002-error stopped the tunnel so the silence makes the
# server purge its stale paths, start it again once the silence lasted long
# enough. Done here because this runs on every pass of every tracked
# interface, while 002-error only acts on status transitions
gtudp_holddown="$(cat /tmp/omr-tracker-gtudp-holddown 2>/dev/null)"
if [ -n "$gtudp_holddown" ]; then
	if [ "$(($(date +%s) - gtudp_holddown))" -ge 150 ]; then
		_log "Glorytun UDP hold-down finished, start it again"
		rm -f /tmp/omr-tracker-gtudp-holddown
		# Next hold-down only if the tunnel stays in error for 15 more minutes
		date +%s > /tmp/omr-tracker-gtudp-outage-start
		date +%s > /tmp/omr-tracker-gtudp-error-since
		date +%s > /tmp/omr-tracker-gtudp-last-restart
		"$gtudp_init" start >/dev/null 2>&1
	else
		# The silence must not be broken: stop the tunnel again if
		# something else (respawn, hotplug, user) restarted it meanwhile
		[ -n "$(pidof glorytun-udp)" ] && "$gtudp_init" stop >/dev/null 2>&1
		exit 0
	fi
fi

multipath_config=$(uci -q get "openmptcprouter.$OMR_TRACKER_INTERFACE.multipath")
[ -z "$multipath_config" ] && multipath_config=$(uci -q get "network.$OMR_TRACKER_INTERFACE.multipath" || echo "off")

# The master WAN carries the tunnel like an "on" one (pref 1): without it a
# router left with only its master usable has no Glorytun UDP path at all
case "$multipath_config" in
	on|master|backup) ;;
	*) exit 0 ;;
esac

download="$(uci -q get network.$OMR_TRACKER_INTERFACE.downloadspeed)"
[ -z "$download" ] && download="$(uci -q get sqm.$OMR_TRACKER_INTERFACE.download)"
upload="$(uci -q get network.$OMR_TRACKER_INTERFACE.uploadspeed)"
[ -z "$upload" ] && upload="$(uci -q get sqm.$OMR_TRACKER_INTERFACE.upload)"

# Whether glorytun-udp has a running path from the local address $1. The
# address is compared whole ("path  <local>  <remote>.<port>  <status> ..."):
# a grep for 100.64.12.3 also matched a running 100.64.12.34 path, or that
# address in another path's remote column, and this WAN's path was then
# never added
_gtudp_path_running() {
	glorytun-udp path 2>/dev/null | awk -v a="$1" '$1 == "path" && $2 == a && $4 == "running" { f = 1 } END { exit !f }'
}

glorytunudppath() {
	gtudp_port="$(uci -q get glorytun-udp.vpn.port)"
	gtudp_dev="$(uci -q get glorytun-udp.vpn.dev)"
	addpath() {
		serverip=$1
		[ "$(uci -q get glorytun-udp.vpn.rateauto)" = "1" ] && rate_mode="auto" || rate_mode="fixed"
		if [ "$download" != "0" ] && [ "$download" != "" ] && [ "$upload" != "0" ] && [ "$upload" != "" ]; then
			# The speeds are kbit/s, glorytun-udp's rate is bytes/s (it shows
			# tx_max_rate * 8 as bits/s, and 12500000 below is 100 Mbit/s):
			# *1000 set every path 8 times faster than its link
			rate_tx=$((upload*125))
			rate_rx=$((download*125))
		else
			rate_tx=12500000
			rate_rx=12500000
		fi
		# One resolved address per family: serverip was overwritten with the
		# IPv4 address, so for a server given by name "resolveip -6" then got
		# an IPv4 literal and the IPv6 path was never added, and the backup
		# pref below looked up each family from the other's address. Only
		# resolved when needed: a running path must not wait on DNS
		serverip4=""
		serverip6=""
		if [ "$OMR_TRACKER_DEVICE_IP" != "" ] && [ "$(uci -q get openmptcprouter.$OMR_TRACKER_INTERFACE.vpn)" != "1" ] && [ "$(pgrep -f glorytun-udp)" != "" ] && ! _gtudp_path_running "$OMR_TRACKER_DEVICE_IP"; then
			serverip4="$(resolveip -4 -t 5 "$serverip" | head -n 1 | tr -d '\n')"
			[ -n "$serverip4" ] && glorytun-udp path addr $OMR_TRACKER_DEVICE_IP to addr "$serverip4" port ${gtudp_port} dev ${gtudp_dev} set up rate $rate_mode tx $rate_tx rx $rate_rx pref 1 > /dev/null 2>&1
		fi
		if [ "$OMR_TRACKER_DEVICE_IP6" != "" ] && [ "$(uci -q get openmptcprouter.$OMR_TRACKER_INTERFACE.vpn)" != "1" ] && [ "$(pgrep -f glorytun-udp)" != "" ] && ! _gtudp_path_running "$OMR_TRACKER_DEVICE_IP6"; then
			serverip6="$(resolveip -6 -t 5 "$serverip" | head -n 1 | tr -d '\n')"
			[ -n "$serverip6" ] && glorytun-udp path addr "$OMR_TRACKER_DEVICE_IP6" to addr "$serverip6" port ${gtudp_port} dev ${gtudp_dev} set up rate $rate_mode tx $rate_tx rx $rate_rx pref 1 > /dev/null 2>&1
		fi
		[ "$multipath_config" = "backup" ] && [ "$(pgrep -f glorytun-udp)" != "" ] && {
			if [ -n "$OMR_TRACKER_DEVICE_IP" ]; then
				[ -z "$serverip4" ] && serverip4="$(resolveip -4 -t 5 "$serverip" | head -n 1 | tr -d '\n')"
				[ -n "$serverip4" ] && glorytun-udp path addr $OMR_TRACKER_DEVICE_IP to addr "$serverip4" port ${gtudp_port} dev ${gtudp_dev} set pref 125 > /dev/null 2>&1
			fi
			if [ -n "$OMR_TRACKER_DEVICE_IP6" ]; then
				[ -z "$serverip6" ] && serverip6="$(resolveip -6 -t 5 "$serverip" | head -n 1 | tr -d '\n')"
				[ -n "$serverip6" ] && glorytun-udp path addr "$OMR_TRACKER_DEVICE_IP6" to addr "$serverip6" port ${gtudp_port} dev ${gtudp_dev} set pref 125 > /dev/null 2>&1
			fi
		}
	}
	config_list_foreach $1 ip addpath
}

config_load openmptcprouter
config_foreach glorytunudppath server

# Set down any path whose local address is no longer assigned to any
# interface (stale path left after an IP change)
if [ -n "$(pgrep -f glorytun-udp)" ]; then
	gtudp_dev="$(uci -q get glorytun-udp.vpn.dev)"
	[ -z "$gtudp_dev" ] && gtudp_dev="tun0"
	for gtudp_path_addr in $(glorytun-udp path 2>/dev/null | awk '$1 == "path" {print $2}'); do
		[ -n "$(ip -o addr show 2>/dev/null | grep -F " ${gtudp_path_addr}/")" ] && continue
		_log "Set down stale Glorytun UDP path $gtudp_path_addr (address no longer assigned)"
		glorytun-udp path addr "$gtudp_path_addr" dev "$gtudp_dev" set down > /dev/null 2>&1
	done
fi

exit 0
