#!/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 do commands when an interface is detected as up

if [ "$OMR_TRACKER_STATUS" = "ERROR" ]; then
	exit 0
fi

#if [ "$OMR_TRACKER_INTERFACE" != "omrvpn" ] && [ "$OMR_TRACKER_INTERFACE" != "omr6in4" ] && [ "$OMR_TRACKER_PREV_STATUS" = "$OMR_TRACKER_STATUS" ] && [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$OMR_TRACKER_PREV_DEVICE_IP" = "$OMR_TRACKER_DEVICE_IP" ] && [ "$OMR_TRACKER_PREV_DEVICE_IP6" = "$OMR_TRACKER_DEVICE_IP6" ] && (([ -n "$OMR_TRACKER_DEVICE_GATEWAY" ] && [ -n "$(ip r show table 991337)" ]) || ([ -n "$OMR_TRACKER_DEVICE_GATEWAY6" ] && [ -n "$(ip -6 r show table 6991337)" ])); then
#	exit 0
#fi

SETROUTE=false
SETROUTE6=false

. "${OMR_LIB_DIR:-/usr/share/omr/lib}/common-post-tracking.sh"

# One clock read for the whole run, the lock's timestamp included
_omr_now=$(date +%s)

# Prevent overlapping 003-up runs for the same interface.
# The name is built by the shell rather than printf|tr, and the directory
# only created when missing: this runs on every cycle of every interface.
lock_base_dir="${OMR_TRACKER_LOCK_DIR:-/var/run/omr-tracker}"
lock_iface="${OMR_TRACKER_INTERFACE:-global}"
lock_iface="${lock_iface//[!A-Za-z0-9_.-]/_}"
lock_dir="${lock_base_dir}/003-up.${lock_iface}.lock"
# A run can't legitimately take this long: past it the pid in the lock was
# recycled by an unrelated process and the lock is stale.
lock_max_age=600

[ -d "$lock_base_dir" ] || mkdir -p "$lock_base_dir" >/dev/null 2>&1

_acquire_003_up_lock() {
	mkdir "$lock_dir" 2>/dev/null || return 1
	printf '%s\n' "${BASHPID:-$$}" >"$lock_dir/pid"
	printf '%s\n' "$_omr_now" >"$lock_dir/ts" 2>/dev/null
	return 0
}

if ! _acquire_003_up_lock; then
	lock_stale=false
	lock_pid="$(cat "$lock_dir/pid" 2>/dev/null)"
	lock_ts="$(cat "$lock_dir/ts" 2>/dev/null)"
	case "$lock_ts" in
		''|*[!0-9]*) lock_ts="" ;;
	esac
	if [ -z "$lock_ts" ]; then
		# Holder died before writing the timestamp (e.g. killed during a config
		# re-apply): nothing can age the lock safely.
		lock_stale=true
	elif [ "$(($(date +%s) - lock_ts))" -ge "$lock_max_age" ]; then
		lock_stale=true
	elif [ -z "$lock_pid" ]; then
		lock_stale=true
	elif ! kill -0 "$lock_pid" 2>/dev/null; then
		lock_stale=true
	fi
	if [ "$lock_stale" = true ]; then
		_log "Removing stale 003-up lock on ${OMR_TRACKER_INTERFACE:-global}"
		rm -rf "$lock_dir" >/dev/null 2>&1
		if ! _acquire_003_up_lock; then
			_log "Skip overlapping 003-up run on ${OMR_TRACKER_INTERFACE:-global}"
			exit 0
		fi
	else
		_log "Skip overlapping 003-up run on ${OMR_TRACKER_INTERFACE:-global}"
		exit 0
	fi
fi

cleanup_003_up_lock() {
	rm -rf "$lock_dir" >/dev/null 2>&1
}
trap cleanup_003_up_lock EXIT INT TERM


#if [ -n "$RANDOM" ]; then
#	sleep `expr $RANDOM % 10`
#else
#	sleep `awk 'BEGIN{srand();print int(rand()*9)}'`
#fi


# Get the current multipath status
multipath_status="off"
_omr_uci_get_var _globals_multipath network.globals.multipath
if [ -e "/sys/class/net/$OMR_TRACKER_DEVICE" ] && [ "$_globals_multipath" = "enable" ]; then
	case "$(multipath "$OMR_TRACKER_DEVICE")" in
		*default*)      multipath_status="on"       ;;
		*backup*)       multipath_status="backup"   ;;
		*handover*)     multipath_status="handover" ;;
	esac
#	if [ "$(multipath -v $OMR_TRACKER_DEVICE)" = "NOK" ] && [ "$multipath_status" != "off" ]; then
#		_log "Incorrect MPTCP endpoint configuration, fixing..."
#		multipath off >/dev/null 2>&1
#		multipath ${multipath_status} >/dev/null 2>&1
#	fi
fi

default_gw=$(ip route get 1.1.1.1 2>/dev/null | awk '/via/ {print $3}')
[ -z "$default_gw" ] && default_gw=$(ip route show default metric 0 | awk '/default/ {print $3}')
# Only compared against the interface's IPv6 gateway below: skip the two
# lookups when the interface has none. "ip -6 route get" prints
# "<dst> from :: via <gateway> dev <device> ...", so the gateway isn't the
# third field; it is kept with its device, as an IPv6 gateway is a
# link-local address that the router of another WAN can have too.
default_gw6=""
if [ -n "$OMR_TRACKER_DEVICE_GATEWAY6" ]; then
	default_gw6=$(ip -6 route get 2606:4700:4700::1111 2>/dev/null | awk "$_omr_awk_via_dev")
	[ -z "$default_gw6" ] && default_gw6=$(ip -6 route show default metric 0 | awk "$_omr_awk_via_dev")
fi
#current_interface_gw=$(uci -q get "network.$OMR_TRACKER_INTERFACE.gateway")

# The main-table default route at metric 1, read once per run: both the
# master and the non-master balancing blocks check whether the tracked
# device is already one of its nexthops.
_omr_def_m1_loaded=""
_omr_default_m1_has_device() {
	if [ -z "$_omr_def_m1_loaded" ]; then
		_omr_def_m1="$(ip route show default metric 1 2>/dev/null)"
		_omr_def_m1_loaded=1
	fi
	_omr_route_has_dev "$_omr_def_m1" "$1"
}

# Has device $1 the weight of its interface in the metric 1 route? A change
# of weight alone (a master set back to on, or the master elected while the
# configured one was disabled set back to on when it returns) leaves every
# device in the route, so nothing rebuilt it and the old master kept its
# weight of 100. No weight shown is a single nexthop: the kernel drops it.
_omr_default_m1_weight_ok() {
	local _cfg _want _line _w=""
	_omr_get_multipath_config_var _cfg "$OMR_TRACKER_INTERFACE"
	_omr_route_fragment_var _want "$OMR_TRACKER_INTERFACE" "$_cfg" "" "$1"
	_want="${_want##* weight }"
	_omr_default_m1_has_device "$1" || return 0
	while read -r _line; do
		case " $_line " in
			*" dev $1 "*)
				case "$_line" in
					*" weight "*) _w="${_line##* weight }"; _w="${_w%% *}" ;;
				esac
				break
				;;
		esac
	done <<EOF
$_omr_def_m1
EOF
	[ -z "$_w" ] || [ "$_w" = "$_want" ]
}

# Is the metric 1 route a resilient group or a plain route, as
# multipath_resilient asks (see _omr_balanced_route_replace)? With every
# device in it and the right weights nothing rebuilt it, so an upgrade or
# a change of the setting left the other form in place.
_omr_default_m1_form_ok() {
	if [ -z "$_omr_def_m1_loaded" ]; then
		_omr_def_m1="$(ip route show default metric 1 2>/dev/null)"
		_omr_def_m1_loaded=1
	fi
	[ -n "$_omr_def_m1" ] || return 0
	case " $_omr_def_m1 " in
		*[[:space:]]nhid[[:space:]]*) _omr_balanced_resilient ;;
		*) ! _omr_balanced_resilient ;;
	esac
}

# Set in the persistent config only when the value differs, and remember
# it: the commits below are for this script's own edits, not for whatever
# other processes have pending in the shared /tmp/.uci (the runtime values
# go to /var/state through omr_state_set and never need a commit)
_omr_cfg_changed=""
_omr_cfg_set() {
	[ "$(command uci -q get "$1")" = "$2" ] && return 0
	uci -q set "$1=$2" && _omr_cfg_changed=1
}

# Compute the IPv4 balanced routes (metric 1, and 999 for the backup
# interfaces) from the interfaces' state, and flag in _omr_bal_m1 and
# _omr_bal_m999 the ones that differ from the current routes. Returns 0
# when one does. It runs once unlocked, so the steady state costs no lock,
# then again under the balancing lock (see _omr_balancing_lock) right
# before installing, so what is installed is the state the other trackers
# have set by then.
_omr_up_balancing_diff() {
	routesbalancing=""
	routesbalancingbackup=""
	nbintf=0
	nbintfb=0
	_omr_bal_m1=""
	_omr_bal_m999=""
	config_load network
	config_foreach set_route_balancing interface
	[ -n "$routesbalancing" ] && ! _default_route_matches 1 "$routesbalancing" "$nbintf" false && _omr_bal_m1=1
	[ -n "$routesbalancingbackup" ] && ! _default_route_matches 999 "$routesbalancingbackup" "$nbintfb" false && _omr_bal_m999=1
	[ -n "$_omr_bal_m1$_omr_bal_m999" ]
}

# debug is set by common-post-tracking.sh

initcwrwnd=""
if _omr_if_up "$OMR_TRACKER_INTERFACE"; then interface_up="true"; else interface_up="false"; fi

_omr_uci_get_var _intf_state "openmptcprouter.$OMR_TRACKER_INTERFACE.state"
if [ "$_intf_state" != "up" ]; then
	omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE state 'up'
#	uci -q commit openmptcprouter
fi


if [ "$OMR_TRACKER_INTERFACE" = "glorytun" ] || [ "$OMR_TRACKER_INTERFACE" = "omrvpn" ] || [ "$OMR_TRACKER_INTERFACE" = "omr6in4" ]; then
	if [ "$(_omr_uci_get network.omrvpn.disabled)" = "1" ]; then
		_log "VPS/tunnel disabled by user, ignore $OMR_TRACKER_INTERFACE up event"
		exit 0
	fi
	if [ "$OMR_TRACKER_INTERFACE" = "omrvpn" ]; then
		omr_state_set openmptcprouter omr vpn 'up'
	fi
	# Tunnel is up again, reset the Glorytun UDP outage state
	rm -f /tmp/omr-tracker-gtudp-error-since /tmp/omr-tracker-gtudp-outage-start /tmp/omr-tracker-gtudp-holddown
	if [ "$(uci -q get openmptcprouter.settings.shadowsocksudp)" = "1" ] && [ "$(uci -q get shadowsocks-libev.sss0.disabled)" != "1" ] && [ -n "$(uci -q get shadowsocks-libev.sss0)" ] && [ "$(uci -q get shadowsocks-libev.ss_rules.redir_udp)" = "hi1" ]; then
		_log "Tunnel up disable use of ShadowSocks for UDP"
		uci -q delete shadowsocks-libev.ss_rules.redir_udp
		if /etc/init.d/shadowsocks-libev rules_exist ; then
			/etc/init.d/shadowsocks-libev rules_down >/dev/null 2>&1
			/etc/init.d/shadowsocks-libev rules_up >/dev/null 2>&1
		fi
	fi
	if { [ "$default_gw" != "$OMR_TRACKER_DEVICE_GATEWAY" ] || [ "$default_gw" = "" ]; } && [ "$OMR_TRACKER_DEVICE_GATEWAY" != "" ]; then
		[ "$debug" = "true" ] && _log "Default gw : $default_gw - Current route: $(ip r)"
		if [ "$(uci -q get openvpn.omr2.enabled)" != "1" ]; then
			_log "Tunnel up : Replace default route by $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE (was $default_gw)"
			ip route replace default scope global via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE $initcwrwnd >/dev/null 2>&1
		else
			_log "Tunnel up : Replace default route by load balancing to $OMR_TRACKER_DEVICE_GATEWAY"
			set_vpn_balancing_routes "$OMR_TRACKER_DEVICE_GATEWAY"
		fi
		if [ "$(pgrep -f openmptcprouter-vps)" = "" ]; then
			_omr_detach /etc/init.d/openmptcprouter-vps restart
		fi
		[ -n "$(uci -q get shadowsocks-libev.sss0)" ] && [ "$(uci -q get shadowsocks-libev.sss0.disabled)" != "1" ] && conntrack -D -p udp >/dev/null 2>&1
		[ "$debug" = "true" ] && _log "New route: $(ip r)"
	fi

	# Set VPN MTU
	# Not for mqvpn: it negotiates and dynamically re-applies its own tun MTU
	# from the actual QUIC/MASQUE datagram budget (mqvpn_client.c
	# cb_dgram_mss_updated -> cbs.mtu_updated -> mqvpn_tun_set_mtu). A static
	# UCI mtu or omr-mtu's raw path-MTU probe below knows nothing about that
	# per-datagram framing overhead, so it can set a wrong value; worse, an
	# external `ip link set ... mtu` here desyncs mqvpn's own
	# last_notified_mtu tracking from the interface's real state, silencing
	# further mqvpn-side corrections until the next reconnect (live-diagnosed
	# 2026-08-27 as the cause of large UDP through mqvpn getting silently
	# dropped with no signal back to the LAN client).
	_omr_uci_get_var _vpn_lc "openmptcprouter.$OMR_TRACKER_INTERFACE.lc"
	if [ -n "$OMR_TRACKER_LATENCY" ] && [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$(_omr_uci_get openmptcprouter.settings.vpn)" != "mqvpn" ] && { [ -z "$_vpn_lc" ] || [ $((_omr_now - ${_vpn_lc:-0})) -gt 3600 ]; }; then
		#_log "Set VPN MTU"
		serverip=$(uci -q get shadowsocks-libev.sss0.server || uci -q get shadowsocks-rust.sss0.server || uci -q get v2ray.omrout.s_vless_address)
		if [ -n "$serverip" ]; then
			_omr_resolve_var serverip_resolved "-4" "$serverip"
			if [ -n "$serverip_resolved" ] && [ "$serverip_resolved" != "192.168.1.3" ] && [ "$serverip_resolved" != "127.0.0.1" ]; then
				serverip="$serverip_resolved"
			else
				serverip=""
			fi
		fi
		_omr_cfg_set openmptcprouter.$OMR_TRACKER_INTERFACE "interface"
		# Measured latency is volatile state, not configuration (see omr-state.sh)
		omr_state_set openmptcprouter "$OMR_TRACKER_INTERFACE" latency "$OMR_TRACKER_LATENCY"
		#if [ "$(uci -q get glorytun.vpn.enable)" != "1" ] || [ "$(uci -q get glorytun-udp.vpn.enable)" != "1" ]; then
			# The MTU configured on the interface, else on its device section,
			# in the order 020-status takes them
			mtu=$(_omr_uci_get network.${OMR_TRACKER_INTERFACE}.mtu)
			if [ -z "$mtu" ]; then
				OMR_NETWORK_DEVICE=$(find_network_device ${OMR_TRACKER_INTERFACE})
				[ -n "$OMR_NETWORK_DEVICE" ] && mtu=$(_omr_uci_get network.${OMR_NETWORK_DEVICE}.mtu)
			fi
			if [ -n "$mtu" ]; then
				_omr_cfg_set openmptcprouter.${OMR_TRACKER_INTERFACE}.mtu "$mtu"
				[ -n "$mtu" ] && [ "$(ip --json link show dev $OMR_TRACKER_DEVICE | jsonfilter -e '@[0].mtu' | tr -d '\n')" != "$mtu" ] && ip link set dev $OMR_TRACKER_DEVICE mtu $mtu > /dev/null 2>&1
				omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE lc "$_omr_now"
			elif [ -z "$(_omr_uci_get openmptcprouter.${OMR_TRACKER_INTERFACE}.mtu)" ] && [ -n "$OMR_TRACKER_DEVICE_IP" ] && [ -n "$OMR_TRACKER_DEVICE" ]; then
				# IPv4 probe: an IPv6 server address here would make
				# omr-mtu ping the wrong family and report nothing
				_mtu_target="1.1.1.1"
				case "$serverip" in
					''|127.0.0.1|*:*) ;;
					*) _mtu_target="$serverip" ;;
				esac
				mtu=$(omr-mtu $OMR_TRACKER_DEVICE_IP $_mtu_target)
				case "$mtu" in ''|*[!0-9]*) mtu="" ;; esac
				[ -n "$mtu" ] && {
					_omr_cfg_set openmptcprouter.${OMR_TRACKER_INTERFACE}.mtu "$mtu"
					# Changing the MTU resets the carrier on several NIC
					# drivers, so only touch the link when it differs
					[ "$(ip --json link show dev $OMR_TRACKER_DEVICE | jsonfilter -q -e '@[0].mtu' | tr -d '\n')" != "$mtu" ] && \
						ip link set dev $OMR_TRACKER_DEVICE mtu $mtu > /dev/null 2>&1
					omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE lc "$_omr_now"
				}
			else
				omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE lc "$_omr_now"
			fi
		#else
		#	uci -q set openmptcprouter.$OMR_TRACKER_INTERFACE.lc=$(date +"%s")
		#fi
	fi
	[ -n "$_omr_cfg_changed" ] && uci -q commit openmptcprouter
	# The VPS DNATs the port forwards to the router's tunnel address. The
	# omrvpn hotplug sends a new one once, on ifup; when the VPS API didn't
	# answer then (the VPS had just restarted), or the address changed with
	# no ifup, the forwards went to the old one until the hourly refresh.
	# set_vpn_ip records what every server accepted; retried once a minute.
	if [ "$OMR_TRACKER_INTERFACE" = "omrvpn" ] && [ -n "$OMR_TRACKER_DEVICE_IP" ] && [ "$(_omr_uci_get openmptcprouter.settings.vpn)" != "none" ]; then
		_omr_uci_get_var _vpnip_sent "openmptcprouter.omr.vpnip_sent"
		_omr_uci_get_var _vpnip_try "openmptcprouter.omr.vpnip_try"
		if [ "$_vpnip_sent" != "$OMR_TRACKER_DEVICE_IP" ] && [ $((_omr_now - ${_vpnip_try:-0})) -ge 60 ] && [ -z "$(pgrep -f openmptcprouter-vps)" ]; then
			omr_state_set openmptcprouter omr vpnip_try "$_omr_now"
			_omr_detach /etc/init.d/openmptcprouter-vps set_vpn_ip
		fi
	fi
	nbserver=0
	piholeenabled=0
	# Only for the VPNs openmptcprouter-vps' _get_pihole can point dnsmasq
	# through: with another one the condition below never clears, and the
	# VPS was asked again on every cycle
	_pihole_vpn=0
	case "$(_omr_uci_get openmptcprouter.settings.vpn)" in
		glorytun_tcp|glorytun_udp|openvpn|mlvpn|dsvpn|mqvpn) _pihole_vpn=1 ;;
	esac
	if [ "$_pihole_vpn" = "1" ] && [ "$(_omr_uci_get openmptcprouter.settings.pihole_auto_conf)" != "0" ]; then
		config_load openmptcprouter
		config_foreach enable_pihole server
		#config_foreach delete_server_default_route server
		[ "$nbserver" = "$piholeenabled" ] && /etc/init.d/openmptcprouter-vps set_pihole >/dev/null 2>&1
	fi
	#ubus call network reload
	exit 0
fi

if [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$(_omr_uci_get openmptcprouter.$OMR_TRACKER_INTERFACE.vpn)" = "1" ]; then
	if [ "$(_omr_uci_get openmptcprouter.$OMR_TRACKER_INTERFACE.state)" != "up" ]; then
		omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE state 'up'
	fi
	exit 0
fi
# Only touch uci when there is something to remove: a blind delete costs a
# fork on every cycle and invalidates the cached config for nothing
_omr_uci_get_var _restart_intf "openmptcprouter.$OMR_TRACKER_INTERFACE.restart_intf" && omr_state_del openmptcprouter $OMR_TRACKER_INTERFACE restart_intf

if [ "$OMR_TRACKER_PREV_STATUS" != "" ] && [ "$OMR_TRACKER_PREV_STATUS" != "$OMR_TRACKER_STATUS" ] && [ -n "$OMR_TRACKER_INTERFACE" ]; then
	_log "$OMR_TRACKER_INTERFACE ($OMR_TRACKER_DEVICE) switched up"
	mail_alert="$(uci -q get omr-tracker.$OMR_TRACKER_INTERFACE.mail_alert)"
	[ -z "$mail_alert" ] && mail_alert="$(uci -q get omr-tracker.defaults.mail_alert)"
	mail_to="$(uci -q get mail.default.to | tr -d '\r\n')"
	case "$mail_to" in
		-*|*[[:space:]]*) mail_to="" ;;
	esac
	[ "$mail_alert" = "1" ] && [ -n "$mail_to" ] && {
		OMR_SYSNAME="$(uci -q get system.@system[0].hostname)"
		if [ "$(uci -q get omr-tracker.defaults.mail_up_subject)" != "" ] && [ "$(uci -q get omr-tracker.defaults.mail_up_message)" != "" ]; then
			mail_subject="$(uci -q get omr-tracker.defaults.mail_up_subject)"
			mail_subject=`echo $mail_subject | sed -e "s/%SYSNAME%/$OMR_SYSNAME/g" -e "s/%INTERFACE%/$OMR_TRACKER_INTERFACE/g" -e "s/%DEVICE%/$OMR_TRACKER_DEVICE/g" -e "s/%MESSAGE%/$OMR_TRACKER_STATUS_MSG/g"`
			mail_subject="$(printf '%s' "$mail_subject" | tr -d '\r\n')"
			mail_message="$(uci -q get omr-tracker.defaults.mail_up_message)"
			mail_message=`echo $mail_message | sed -e "s/%SYSNAME%/$OMR_SYSNAME/g" -e "s/%INTERFACE%/$OMR_TRACKER_INTERFACE/g" -e "s/%DEVICE%/$OMR_TRACKER_DEVICE/g" -e "s/%MESSAGE%/$OMR_TRACKER_STATUS_MSG/g"`
			printf 'Subject: %s\n\n%s\n' "$mail_subject" "$mail_message" | sendmail "$mail_to"
		else
			mail_subject="$(printf '%s' "$OMR_SYSNAME: $OMR_TRACKER_INTERFACE ($OMR_TRACKER_DEVICE) is up" | tr -d '\r\n')"
			printf 'Subject: %s\n\nDetected that connection %s (%s) is up again.\n' "$mail_subject" "$OMR_TRACKER_INTERFACE" "$OMR_TRACKER_DEVICE" | sendmail "$mail_to"
		fi
	}
	script_alert_up="$(uci -q get omr-tracker.$OMR_TRACKER_INTERFACE.script_alert_up)"
	[ -n "$script_alert_up" ] && eval $script_alert_up
	env -i ACTION="connecting" INTERFACE="$OMR_TRACKER_INTERFACE" DEVICE="$OMR_TRACKER_DEVICE" /sbin/hotplug-call iface >/dev/null 2>&1
fi
# The state before this run: it is set to up at the top of the script, so
# reading it again here never matched and the flush never ran
if [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$_intf_state" != "up" ]; then
	dns_flush
fi

if [ -n "$OMR_TRACKER_INTERFACE" ]; then
	_omr_uci_get_var multipath_config "openmptcprouter.$OMR_TRACKER_INTERFACE.multipath"
	_omr_uci_get_var _multipath_setting "network.$OMR_TRACKER_INTERFACE.multipath"
	_omr_uci_get_var _multipathvpn "openmptcprouter.$OMR_TRACKER_INTERFACE.multipathvpn"
	# openmptcprouter.<iface>.multipath is the *effective* mode, network.<iface>.multipath
	# the one the user configured. OMR itself only ever moves the effective copy
	# between "on" and "master" (dynamic master election in 021-latencies) or parks
	# it at "off" for MPTCP over VPN and for a disabled interface. Any other
	# difference means the effective copy is stale, and since it wins below the
	# interface would keep being forced back to that stale mode forever: a WAN once
	# set to "backup" stayed backup whatever the wizard/MPTCP page was set to
	# afterwards, because LuCI drops network.<iface>.multipath when the selected
	# value equals the option default and mptcp's init then restored the old
	# effective value into it (discussion #4368).
	_omr_uci_get_var _multipath_disabled "network.$OMR_TRACKER_INTERFACE.disabled"
	if [ -n "$_multipath_setting" ] && [ "$_multipath_setting" != "$multipath_config" ] && \
		[ "$_multipathvpn" != "1" ] && [ "$_multipath_disabled" != "1" ]; then
		case "${multipath_config}/${_multipath_setting}" in
			on/master|master/on) ;;
			*)
				_log "Multipath mode of $OMR_TRACKER_INTERFACE is $multipath_config but configured as $_multipath_setting, resync"
				_omr_cfg_set openmptcprouter.$OMR_TRACKER_INTERFACE.multipath "$_multipath_setting"
				multipath_config="$_multipath_setting"
			;;
		esac
	fi
	[ -z "$multipath_config" ] && multipath_config="${_multipath_setting:-off}"
	[ "$_multipathvpn" = "1" ] && {
		_omr_uci_get_var _mptcpovervpn openmptcprouter.settings.mptcpovervpn
		[ "$_mptcpovervpn" = "openvpn" ] && _omr_uci_get_var multipath_config_route "openmptcprouter.ovpn${OMR_TRACKER_INTERFACE}.multipath" "off"
		[ "$_mptcpovervpn" = "wireguard" ] && _omr_uci_get_var multipath_config_route "openmptcprouter.wg${OMR_TRACKER_INTERFACE}.multipath" "off"
	}
fi

_omr_uci_get_var master_setting openmptcprouter.settings.master
_omr_uci_get_var defaultgw_setting openmptcprouter.settings.defaultgw
_omr_uci_get_var omrvpn_intf network.omrvpn.device "tun0"
[ -z "$omrvpn_intf" ] && _omr_uci_get_var omrvpn_intf network.omrvpn.ifname "tun0"
if [ "$multipath_config" = "master" ]; then
	if [ "$master_setting" != "balancing" ]; then
		if { [ "$default_gw" != "$OMR_TRACKER_DEVICE_GATEWAY" ] || [ "$default_gw" = "" ]; } && [ "$OMR_TRACKER_DEVICE_GATEWAY" != "" ]; then
			if [ -n "$omrvpn_intf" ] && [ "$(ip route show default metric 0 | grep $omrvpn_intf)" = "" ] && [ "$defaultgw_setting" != "0" ]; then
				_log "Master up : Replace default route by $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE"
				ip route replace default scope global metric 1 via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE $initcwrwnd >/dev/null 2>&1
			fi
			config_load openmptcprouter
			#if [ "$master_setting" = "balancing" ]; then
			#	config_foreach set_server_all_routes server
			if [ "$master_setting" != "failover" ]; then
				config_foreach set_server_default_route server
				#config_foreach set_server_default_route6 server
			fi
			ip route flush cache >/dev/null 2>&1
		fi
		if { [ "$default_gw6" != "$OMR_TRACKER_DEVICE_GATEWAY6 $OMR_TRACKER_DEVICE" ] || [ "$default_gw6" = "" ]; } && [ "$OMR_TRACKER_DEVICE_GATEWAY6" != "" ]; then
			if [ -n "$omrvpn_intf" ] && [ "$(ip route show default | awk '/default/ {print $5}' | grep $omrvpn_intf)" = "" ] && [ "$defaultgw_setting" != "0" ]; then
				_log "Master up : Replace default route by $OMR_TRACKER_DEVICE_GATEWAY6 dev $OMR_TRACKER_DEVICE"
				# metric 1 as the IPv4 route above and set_route6: without one
				# it went to 1024, behind the per-interface routes (metric
				# 6<metric>), so IPv6 never followed the master and this ran
				# again on every cycle
				ip -6 route replace default scope global metric 1 via $OMR_TRACKER_DEVICE_GATEWAY6 dev $OMR_TRACKER_DEVICE >/dev/null 2>&1
			fi
			config_load openmptcprouter
			#if [ "$master_setting" = "balancing" ]; then
			#	config_foreach set_server_all_routes server
			if [ "$master_setting" != "failover" ]; then
				#config_foreach set_server_default_route server
				config_foreach set_server_default_route6 server
			fi
			ip -6 route flush cache >/dev/null 2>&1
		fi
	fi
	if [ "$master_setting" = "balancing" ]; then
		if { { [ -n "$OMR_TRACKER_DEVICE_IP" ] && { ! _omr_default_m1_has_device "$OMR_TRACKER_DEVICE" || ! _omr_default_m1_form_ok; }; } || { [ -n "$OMR_TRACKER_DEVICE_IP6" ] && [ "$(ip -6 route show default metric 1 | grep "$OMR_TRACKER_DEVICE")" = "" ]; }; } && [ "$defaultgw_setting" != "0" ]; then
			if [ -n "$omrvpn_intf" ] && [ -n "$OMR_TRACKER_DEVICE_IP" ] && _omr_up_balancing_diff; then
				_omr_balancing_lock
				_omr_up_balancing_diff
				[ -n "$_omr_bal_m1" ] && {
					_log "Change in routes, set ip route replace default scope global $routesbalancing (omrvpn_intf: $omrvpn_intf)"
					[ "$debug" = "true" ] && _log "Current route: $(ip r)"
					_omr_balanced_route_replace 1 "$routesbalancing"
					_omr_def_m1_loaded=""
					[ "$debug" = "true" ] && _log "New route: $(ip r)"
				}
				[ -n "$_omr_bal_m999" ] && {
					_log "Set backup ip route replace default scope global metric 999 $routesbalancingbackup"
					_omr_balanced_route_replace 999 "$routesbalancingbackup"
				}
				_omr_balancing_unlock
			fi
			if [ -n "$omrvpn_intf" ] && [ -n "$OMR_TRACKER_DEVICE_IP6" ] && [ "$(ip -6 route show default metric 1 | tr -d '\t' | tr -d '\n' | sed 's/ *$//')" != "default via $routesbalancing6" ]; then
				routesbalancing6=""
				routesbalancingbackup6=""
				nbintf6=0
				nbintfb6=0
				config_load network
				config_foreach set_route_balancing6 interface
				[ -n "$routesbalancing6" ] && [ "$(ip -6 r show default metric 1 | grep omr6in4)" = "" ] && ! _default_route_matches 1 "$routesbalancing6" "$nbintf6" true && {
					_log "Set ip -6 route replace default scope global metric 1 $routesbalancing6"
					[ "$debug" = "true" ] && _log "Current route: $(ip -6 r)"
					ip -6 route replace default scope global metric 1 $routesbalancing6 >/dev/null 2>&1
					[ "$debug" = "true" ] && _log "New route: $(ip -6 r)"
				}
				[ -n "$routesbalancingbackup6" ] && ! _default_route_matches 999 "$routesbalancingbackup6" "$nbintfb6" true && {
					_log "Set backup ip -6 route replace default scope global $routesbalancingbackup6"
					ip -6 route replace default scope global metric 999 $routesbalancingbackup6 >/dev/null 2>&1
				}
			fi
			#ip route flush cache >/dev/null 2>&1
			#ip -6 route flush cache >/dev/null 2>&1
		fi
	fi
	if [ -n "$OMR_TRACKER_DEVICE_GATEWAY" ] && [ -n "$OMR_TRACKER_DEVICE" ] && [ "$(ip r show table 991337)" != "default via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE " ]; then
		ip route replace default via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE table 991337 $initcwrwnd >/dev/null 2>&1
		#ip route flush cache >/dev/null 2>&1
	fi
	if [ -n "$OMR_TRACKER_DEVICE_GATEWAY6" ] && [ -n "$OMR_TRACKER_DEVICE" ] && [ "$(ip -6 r show table 6991337)" != "default via $OMR_TRACKER_DEVICE_GATEWAY6 dev $OMR_TRACKER_DEVICE " ]; then
		ip -6 route replace default via $OMR_TRACKER_DEVICE_GATEWAY6 dev $OMR_TRACKER_DEVICE table 6991337 $initcwrwnd >/dev/null 2>&1
		#ip -6 route flush cache >/dev/null 2>&1
	fi
	# Hourly refresh only. A server left with admin_error or get_config is
	# retried by omr-schedule's VPS watchdog, with a back-off: restarting
	# the whole sync from here on every cycle while a VPS stayed unreachable
	# or refused the key logged in back to back, until its fail2ban banned
	# the router
	lc_jitter=$((10 + RANDOM % 10))
	_omr_uci_get_var _lc "openmptcprouter.$OMR_TRACKER_INTERFACE.lc"
	if { [ -n "$OMR_TRACKER_INTERFACE" ] && [ -z "$_lc" ]; } || [ $((_omr_now + lc_jitter - ${_lc:-0})) -gt 3600 ]; then
		[ -z "$(pgrep -f openmptcprouter-vps)" ] && {
			_omr_detach /etc/init.d/openmptcprouter-vps restart
			omr_state_set openmptcprouter "$OMR_TRACKER_INTERFACE" lc "$_omr_now"
			#uci -q commit openmptcprouter.$OMR_TRACKER_INTERFACE
		}
	fi
	multipath_config="on"
fi

if [ "$master_setting" = "balancing" ] && { [ "$multipath_config" = "on" ] || [ "$multipath_config" = "backup" ]; } && [ "$defaultgw_setting" != "0" ]; then
	# A backup interface is checked even when it is in the metric 1 route:
	# that is where an interface switched from on to backup stays otherwise,
	# since nothing else rebuilds that route while all its devices are up.
	# Compare the current route against what we would install instead of
	# grepping it for the tracked device: a backup interface is never in
	# the metric-1 route, so the device grep replaced and logged an
	# already correct route on every tracker cycle (issue #4358). So is a
	# device whose weight is no longer its interface's.
	if [ -n "$omrvpn_intf" ] && [ -n "$OMR_TRACKER_DEVICE_IP" ] && { ! _omr_default_m1_has_device "$OMR_TRACKER_DEVICE" || [ "$multipath_config" = "backup" ] || ! _omr_default_m1_weight_ok "$OMR_TRACKER_DEVICE" || ! _omr_default_m1_form_ok; } && _omr_up_balancing_diff; then
		_omr_balancing_lock
		_omr_up_balancing_diff
		[ -n "$_omr_bal_m1" ] && {
			_log "Change in routes (non-master), set ip route replace default scope global metric 1 $routesbalancing"
			_omr_balanced_route_replace 1 "$routesbalancing"
		}
		[ -n "$_omr_bal_m999" ] && {
			_log "Set backup ip route replace default scope global metric 999 $routesbalancingbackup"
			_omr_balanced_route_replace 999 "$routesbalancingbackup"
		}
		_omr_balancing_unlock
	fi
	# Non-master (on/backup) WAN recovering never resynced the VPS-side
	# port-forward/redirect state (only the master WAN's own up-event did,
	# incidentally, via the block above) - mirror that lc-staleness-gated
	# resync here so a secondary WAN flap also repairs port-forwarding.
	# Servers in error are omr-schedule's, as above.
	lc_jitter=$((10 + RANDOM % 10))
	_omr_uci_get_var _lc "openmptcprouter.$OMR_TRACKER_INTERFACE.lc"
	if { [ -n "$OMR_TRACKER_INTERFACE" ] && [ -z "$_lc" ]; } || [ $((_omr_now + lc_jitter - ${_lc:-0})) -gt 3600 ]; then
		[ -z "$(pgrep -f openmptcprouter-vps)" ] && {
			_omr_detach /etc/init.d/openmptcprouter-vps restart
			omr_state_set openmptcprouter "$OMR_TRACKER_INTERFACE" lc "$_omr_now"
		}
	fi
fi

_omr_uci_get_var intf_metric "network.$OMR_TRACKER_INTERFACE.metric"
if [ "$master_setting" = "balancing" ] && [ -n "$OMR_TRACKER_DEVICE_IP" ]; then
	config_load openmptcprouter
	config_foreach set_server_all_routes server
fi
if [ "$master_setting" = "balancing" ] && [ -n "$OMR_TRACKER_DEVICE_IP6" ]; then
	config_load openmptcprouter
	config_foreach set_server_all_routes6 server
fi
if [ -n "$OMR_TRACKER_DEVICE_IP" ] && [ -n "$OMR_TRACKER_DEVICE_GATEWAY" ]; then
	config_load openmptcprouter
	if [ "$master_setting" != "balancing" ] && [ "$master_setting" != "failover" ]; then
		config_foreach set_server_route server
	fi
	_omr_dev_routes=""
	[ -n "$OMR_TRACKER_DEVICE" ] && _omr_dev_routes="$(ip r show dev $OMR_TRACKER_DEVICE 2>/dev/null)"
	case "$_omr_dev_routes" in *default*) _omr_dev_has_default=1 ;; *) _omr_dev_has_default="" ;; esac
	if [ "$defaultgw_setting" != "0" ] && [ -n "$OMR_TRACKER_DEVICE_GATEWAY" ] && [ -n "$OMR_TRACKER_DEVICE" ] && [ -z "$_omr_dev_has_default" ] && [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$intf_metric" != "" ]; then
		_log "Interface route not yet set, set route ip r add default via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE metric $intf_metric"
		ip r add default via $OMR_TRACKER_DEVICE_GATEWAY dev $OMR_TRACKER_DEVICE metric $intf_metric >/dev/null 2>&1
		ip route flush cache >/dev/null 2>&1
	fi
fi
if [ -n "$OMR_TRACKER_DEVICE_IP6" ] && [ -n "$OMR_TRACKER_DEVICE_GATEWAY6" ]; then
	config_load openmptcprouter
	if [ "$master_setting" != "balancing" ] && [ "$master_setting" != "failover" ]; then
		config_foreach set_server_route6 server
	fi
	if [ "$defaultgw_setting" != "0" ] && [ -n "$OMR_TRACKER_DEVICE_GATEWAY6" ] && [ -n "$OMR_TRACKER_DEVICE" ] && [ "$(ip -6 r show dev $OMR_TRACKER_DEVICE | grep default)" = "" ] && [ -n "$OMR_TRACKER_INTERFACE" ] && [ "$intf_metric" != "" ]; then
		_log "Interface route not yet set, set route ip -6 r add default via $OMR_TRACKER_DEVICE_GATEWAY6 dev $OMR_TRACKER_DEVICE metric 6$intf_metric"
		ip -6 r replace default via $OMR_TRACKER_DEVICE_GATEWAY6 dev $OMR_TRACKER_DEVICE metric 6$intf_metric >/dev/null 2>&1
		ip -6 route flush cache >/dev/null 2>&1
	fi
fi

if [ -n "$OMR_TRACKER_INTERFACE" ] && { [ "$multipath_config" = "on" ] || [ "$multipath_config" = "backup" ]; }; then
	# Make sure the openmptcprouter config exists before writing to it
	# (was a full "uci show | grep" of every package on each cycle)
	[ -e "${UCI_CONFIG_DIR:-/etc/config}/openmptcprouter" ] || touch "${UCI_CONFIG_DIR:-/etc/config}/openmptcprouter" 2>/dev/null
fi
# One sysctl for both the upstream (net.mptcp.enabled) and legacy
# (net.mptcp.mptcp_enabled) keys: -e skips the one the kernel doesn't have
_mptcp_enabled="$(sysctl -qen net.mptcp.enabled net.mptcp.mptcp_enabled 2>/dev/null)"
case "$_mptcp_enabled" in
	*1*) _mptcp_enabled=1 ;;
	*) _mptcp_enabled="" ;;
esac
if [ -n "$_mptcp_enabled" ]; then
	_omr_uci_get_var _multipathvpn "openmptcprouter.$OMR_TRACKER_INTERFACE.multipathvpn"
	_omr_uci_get_var _force_multipath network.globals.mptcp_force_multipath
	_omr_uci_get_var _force_link "network.$OMR_TRACKER_INTERFACE.force_link"
	[ "$_multipathvpn" != "1" ] && [ "$_force_multipath" != "0" ] && [ "$_globals_multipath" = "enable" ] && {
		if [ "$OMR_TRACKER_PREV_STATUS" != "" ] && [ "$OMR_TRACKER_PREV_STATUS" != "$OMR_TRACKER_STATUS" ]; then
			if [ -n "$OMR_TRACKER_DEVICE" ] && [ "$_force_link" != "0" ]; then
				_log "Status change, reload MPTCP config for $OMR_TRACKER_DEVICE"
				/etc/init.d/mptcp reload "$OMR_TRACKER_DEVICE" >/dev/null 2>&1
			fi
		fi
		_omr_uci_get_var _local_ipv4 "openmptcprouter.$OMR_TRACKER_INTERFACE.local_ipv4"
		_omr_uci_get_var _local_ipv6 "openmptcprouter.$OMR_TRACKER_INTERFACE.local_ipv6"
		# Same explicit endpoint id as mptcp's init script (network.<iface>.metric):
		# without it the kernel auto-assigns a new id every time the endpoint is
		# recreated here, so the id a WAN is known by drifts upwards on every
		# status change and the VPS side can no longer pin traffic to a WAN by
		# the subflow remote_id (see "multipath -h", mptcp-dscp/weight sync).
		_omr_uci_get_var _multipath_id "network.$OMR_TRACKER_INTERFACE.metric"
		if [ "$multipath_status" = "$multipath_config" ] && { [ "$_local_ipv4" != "$OMR_TRACKER_DEVICE_IP" ] || [ "$_local_ipv6" != "$OMR_TRACKER_DEVICE_IP6" ]; }; then
			_log "Local IP changed for $OMR_TRACKER_DEVICE ($OMR_TRACKER_INTERFACE) reload Multipath"
			if [ -n "$OMR_TRACKER_DEVICE" ] && [ "$_force_link" != "0" ]; then
				_log "Reload MPTCP config for $OMR_TRACKER_DEVICE"
				/etc/init.d/mptcp reload "$OMR_TRACKER_DEVICE" >/dev/null 2>&1
			fi
			omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE local_ipv4 "$OMR_TRACKER_DEVICE_IP"
			omr_state_set openmptcprouter $OMR_TRACKER_INTERFACE local_ipv6 "$OMR_TRACKER_DEVICE_IP6"
		elif [ "$multipath_status" != "$multipath_config" ]; then
			_log "Multipath $OMR_TRACKER_DEVICE ($OMR_TRACKER_INTERFACE) switched to $multipath_config (from $multipath_status)"
			multipath "$OMR_TRACKER_DEVICE" "$multipath_config" "$_multipath_id" >/dev/null 2>&1
			if [ -n "$OMR_TRACKER_DEVICE" ] && [ "$_force_link" != "0" ]; then
				/etc/init.d/mptcp reload "$OMR_TRACKER_DEVICE" >/dev/null 2>&1
			fi
		elif [ -n "$OMR_TRACKER_DEVICE" ] && ! multipath -i "$OMR_TRACKER_DEVICE" "$_multipath_id"; then
			# Right mode but not the id of this WAN, kept from before a metric
			# change or added by hand: nothing else recreates the endpoint, and
			# the WAN that now has this metric can't get its endpoint added
			_log "MPTCP endpoint of $OMR_TRACKER_DEVICE ($OMR_TRACKER_INTERFACE) doesn't have id $_multipath_id, recreate it"
			multipath "$OMR_TRACKER_DEVICE" "$multipath_config" "$_multipath_id" >/dev/null 2>&1
		fi
	}
fi
[ -n "$_omr_cfg_changed" ] && uci -q commit openmptcprouter

#[ -n "$OMR_TRACKER_DEVICE" ] && OMR_TRACKER_PREV_DEVICE="$OMR_TRACKER_DEVICE"

#ubus call network reload
exit 0
