#!/bin/sh /etc/rc.common
# Copyright (C) 2019 - 2021 Ycarus (Yannick Chabanois) <ycarus@zugaina.org> for OpenMPTCProuter
# Released under GPL 3. See LICENSE for the full terms.

{
	START=99
	STOP=10
	USE_PROCD=1
}

wireguard_restart() {
	local interface=$1
	if [ "$(uci -q get network.${interface}.proto)" = "wireguard" ] && [ -n "$(uci -q get network.${interface}.fwmark)" ]; then
		ifdown ${interface}
		ifup ${interface}
	fi
}

_getremoteip() {
	[ "$(uci -q -P /var/state get openmptcprouter.$1.current)" = "1" ] && {
		remoteserver=$1
		remoteip=$(uci -q get openmptcprouter.$1.ip | awk '{print $1}')
		wg_server_key=$(uci -q get openmptcprouter.$1.wgkey)
	}
}

# The server in use is runtime state: none is marked before omr-tracker's
# first check (at boot) or while every server is down. Take the master's
# address then, as mptcp_over_vpn_restore does, rather than an empty one.
_getmasterip() {
	[ -z "$remoteip" ] && [ "$(uci -q get "openmptcprouter.$1.master")" = "1" ] && {
		remoteserver=$1
		remoteip=$(uci -q get "openmptcprouter.$1.ip" | awk '{print $1}')
		wg_server_key=$(uci -q get "openmptcprouter.$1.wgkey")
	}
}

_getserverip() {
	remoteip=""
	remoteserver=""
	wg_server_key=""
	config_load openmptcprouter
	config_foreach _getremoteip server
	[ -n "$remoteip" ] || config_foreach _getmasterip server
}

# The peer is the server's WireGuard key: a backup's is only known once
# openmptcprouter-vps fetched it, after the tunnel was pointed at it
_wg_peer_key_outdated() {
	[ -n "$wg_server_key" ] && [ "$(uci -q get network.@wireguard_wg$1[0].public_key)" != "$wg_server_key" ]
}

# _tunnel_gone <tunnel interface>: before its network section is deleted.
# netifd removes the interface on the reload, not what other services put
# around it: its tracker instance, the rules of its table (pref 0, set by
# the mptcp init) and its place in the dnsmasq and unbound lists of WANs.
_tunnel_gone() {
	[ -n "$(uci -q get "network.$1")" ] || return 0
	gone_tunnels="$gone_tunnels $1:$(uci -q get "network.$1.ip4table")"
	case " $(uci -q get dhcp.@dnsmasq[0].notinterface) " in
		*" $1 "*) uci -q del_list dhcp.@dnsmasq[0].notinterface="$1" && gone_lists=1 ;;
	esac
	case " $(uci -q get unbound.ub_main.iface_wan) " in
		*" $1 "*) uci -q del_list unbound.ub_main.iface_wan="$1" && gone_lists=1 ;;
	esac
	return 0
}

_tunnels_cleanup() {
	local tunnel table
	if [ "$gone_lists" = "1" ]; then
		uci -q commit dhcp
		uci -q commit unbound
	fi
	for tunnel in $gone_tunnels; do
		table="${tunnel#*:}"
		tunnel="${tunnel%%:*}"
		/etc/init.d/omr-tracker stop "$tunnel" >/dev/null 2>&1
		case "$table" in
			''|*[!0-9]*) continue ;;
		esac
		while ip rule del pref 0 table "$table" 2>/dev/null; do :; done
		while ip -6 rule del pref 0 table "6$table" 2>/dev/null; do :; done
		ip route flush table "$table" >/dev/null 2>&1
	done
}

mptcp_over_vpn() {
	local interface=$1
	case " $(uci -q get firewall.zone_wan.network) " in
		*" $interface "*) nbintf=$(($nbintf+1)) ;;
	esac
	if [ "$(uci -q get openmptcprouter.${interface}.multipathvpn)" = "1" ]; then
		_getserverip

		if [ "$(uci -q get network.${interface})" = "" ]; then
			# The WAN itself was deleted: its tunnels go with it, and
			# need the same cleanup and reload as when MPTCP over VPN is
			# turned off for a WAN that stays
			_tunnel_gone "wg${interface}"
			_tunnel_gone "ovpn${interface}"
			uci -q batch <<-EOF >/dev/null
				delete openmptcprouter.${interface}
				delete network.ovpn${interface}
				delete network.wg${interface}
				delete network.@wireguard_wg${interface}[0]
				delete openvpn.${interface}
				commit openvpn
				delete openmptcprouter.${interface}
				delete openmptcprouter.ovpn${interface}
				delete openmptcprouter.wg${interface}
				commit openmptcprouter
				commit network
				del_list firewall.zone_vpn.network="ovpn${interface}"
				del_list firewall.zone_vpn.network="wg${interface}"
				commit firewall
			EOF
			need_reload=1
			return
		fi
		nbintfvpn=$(($nbintfvpn+1))
		# No address to point the tunnel at: keep the current one
		[ -n "$remoteip" ] || return
		if ([ "$(uci -q get network.ovpn${interface})" = "" ] || [ "$(uci -q get openvpn.${interface}.remote)" != "${remoteip}" ]) && [ "$vpn" = "openvpn" ]; then
			logger -t "MPTCPoverVPN" "Enable MPTCP over VPN for ${interface}"
			id=$(uci -q get network.${interface}.metric)
			localip=$(ubus call network.interface.$interface status | jsonfilter -e '@["ipv4-address"][0].address' | tr -d "\n")
			[ -z "$(uci -q get openmptcprouter.ovpn${interface}.multipath)" ] && multipath=$(uci -q get network.${interface}.multipath)
			[ -n "$(uci -q get openmptcprouter.ovpn${interface}.multipath)" ] && multipath=$(uci -q get openmptcprouter.ovpn${interface}.multipath)
			[ -z "$multipath" ] && multipath="on"
			uci -q batch <<-EOF >/dev/null
				delete network.wg${interface}
				delete openmptcprouter.wg${interface}
				commit openmptcprouter
				commit network
				del_list firewall.zone_vpn.network="wg${interface}"
				commit firewall
			EOF

				uci -q batch <<-EOF >/dev/null
					set network.ovpn${interface}=interface
					set network.ovpn${interface}.device="tun${id}"
					set network.ovpn${interface}.defaultroute='0'
					set network.ovpn${interface}.peerdns='0'
					set network.ovpn${interface}.proto='none'
					set network.ovpn${interface}.ip4table='wan'
					set network.ovpn${interface}.multipath="${multipath}"
					set network.${interface}.multipath='off'
					commit network
					set openvpn.${interface}=openvpn
					set openvpn.${interface}.dev="tun${id}"
					set openvpn.${interface}.cipher='AES-256-CBC'
					set openvpn.${interface}.port='65301'
					set openvpn.${interface}.remote="${remoteip}"
					set openvpn.${interface}.local="${localip}"
					set openvpn.${interface}.lport='0'
					set openvpn.${interface}.auth_nocache='1'
					set openvpn.${interface}.proto='udp'
					set openvpn.${interface}.client='1'
					set openvpn.${interface}.enabled='1'
					set openvpn.${interface}.disable_dco='1'
					set openvpn.${interface}.allow_recursive_routing='1'
					set openvpn.${interface}.key='/etc/luci-uploads/client.key'
					set openvpn.${interface}.cert='/etc/luci-uploads/client.crt'
					set openvpn.${interface}.ca='/etc/luci-uploads/ca.crt'
					commit openvpn
					set openmptcprouter.${interface}.multipath="off"
					set openmptcprouter.${interface}.multipathvpn="1"
					set openmptcprouter.ovpn${interface}="interface"
					set openmptcprouter.ovpn${interface}.multipath="${multipath}"
					set openmptcprouter.ovpn${interface}.vpn="1"
					set openmptcprouter.ovpn${interface}.baseintf="${interface}"
					commit openmptcprouter
					del_list firewall.zone_vpn.network="ovpn${interface}"
					add_list firewall.zone_vpn.network="ovpn${interface}"
					commit firewall
				EOF
			need_reload=1
		elif ([ "$(uci -q get network.wg${interface})" = "" ] || [ "$(uci -q get network.@wireguard_wg${interface}[0].endpoint_host)" != "$remoteip" ] || _wg_peer_key_outdated "$interface") && [ "$vpn" = "wireguard" ]; then
			logger -t "MPTCPoverVPN" "Enable MPTCP over VPN for ${interface}"
			id=$(uci -q get network.${interface}.metric)
			metric=$(uci -q get network.${interface}.metric)
			[ -z "$(uci -q get openmptcprouter.wg${interface}.multipath)" ] && multipath=$(uci -q get network.${interface}.multipath)
			[ -n "$(uci -q get openmptcprouter.wg${interface}.multipath)" ] && multipath=$(uci -q get openmptcprouter.wg${interface}.multipath)
			[ -z "$multipath" ] && multipath="on"
			# The router's keypair is kept across a change of server: every
			# server got this public key from openmptcprouter-vps, a new one
			# on each failover left the one we came back to with a stale peer
			private_key=$(uci -q get network.wg${interface}.private_key)
			public_key=$(uci -q get network.wg${interface}.public_key)
			if [ "${#private_key}" != 44 ] || [ "${#public_key}" != 44 ]; then
				private_key=$(wg genkey | tr -d "\n")
				public_key=$(echo $private_key | wg pubkey | tr -d "\n")
			fi
			# One peer section: "add" on each change of server piled them up
			while [ -n "$(uci -q get network.@wireguard_wg${interface}[0])" ]; do
				uci -q delete network.@wireguard_wg${interface}[0] || break
			done
			uci -q batch <<-EOF >/dev/null
				delete network.ovpn${interface}
				delete openvpn.${interface}
				commit openvpn
				delete openmptcprouter.ovpn${interface}
				commit openmptcprouter
				commit network
				del_list firewall.zone_vpn.network="ovpn${interface}"
				commit firewall
			EOF

				uci -q batch <<-EOF >/dev/null
					set network.wg${interface}=interface
					set network.wg${interface}.nohostroute='1'
					set network.wg${interface}.proto='wireguard'
					set network.wg${interface}.fwmark="0x539${metric}"
					del_list network.wg${interface}.addresses
					add_list network.wg${interface}.addresses='10.255.247.${metric}/24'
					set network.wg${interface}.private_key="${private_key}"
					set network.wg${interface}.gateway="10.255.247.1"
					set network.wg${interface}.public_key="${public_key}"
					set network.wg${interface}.multipath="${multipath}"
					set network.wg${interface}.mtu="1280"
					set network.${interface}.multipath='off'
					add network wireguard_wg${interface}
					set network.@wireguard_wg${interface}[0]=wireguard_wg${interface}
					set network.@wireguard_wg${interface}[0].description="Wireguard on ${interface}"
					set network.@wireguard_wg${interface}[0].endpoint_host="${remoteip}"
					set network.@wireguard_wg${interface}[0].endpoint_port="65311"
					set network.@wireguard_wg${interface}[0].persistent_keepalive="28"
					del_list network.@wireguard_wg${interface}[0].allowed_ips
					add_list network.@wireguard_wg${interface}[0].allowed_ips="0.0.0.0/0"
					set network.@wireguard_wg${interface}[0].public_key="${wg_server_key}"
					commit network
					set openmptcprouter.${interface}.multipath="off"
					set openmptcprouter.${interface}.multipathvpn="1"
					set openmptcprouter.wg${interface}="interface"
					set openmptcprouter.wg${interface}.multipath="${multipath}"
					set openmptcprouter.wg${interface}.vpn="1"
					set openmptcprouter.wg${interface}.baseintf="${interface}"
					commit openmptcprouter
					del_list firewall.zone_vpn.network="wg${interface}"
					add_list firewall.zone_vpn.network="wg${interface}"
					commit firewall
				EOF
			ubus call network reload 2>&1 >/dev/null
			# netifd only compares the interface section: a peer change
			# alone (the server's key, the endpoint) isn't applied by a reload
			ifup wg${interface} >/dev/null 2>&1
			need_reload=1
			# The server only has the peers of the tunnels pointed at it
			# (openmptcprouter-vps): one the tunnels just moved to, a backup
			# on failover, refused them until its next full sync
			wg_peers_server="$remoteserver"
		else
			uci -q batch <<-EOF >/dev/null
				set network.${interface}.multipath='off'
				commit network
				set openmptcprouter.${interface}.multipath="off"
				commit openmptcprouter
			EOF
		fi
	elif [ "$(uci -q get openmptcprouter.ovpn${interface})" != "" ] || [ "$(uci -q get network.wg${interface})" != "" ]; then
		logger -t "MPTCPoverVPN" "Disable MPTCP over VPN for ${interface}"
		# The WAN's mode is kept in ovpn<wan> or wg<wan>, as the tunnel was
		# made: from wg<wan>, a master or backup WAN became "on"
		multipath=$(uci -q get openmptcprouter.ovpn${interface}.multipath)
		[ -z "$multipath" ] && multipath=$(uci -q get openmptcprouter.wg${interface}.multipath)
		[ -z "$multipath" ] && multipath="on"
		_tunnel_gone "wg${interface}"
		_tunnel_gone "ovpn${interface}"
		uci -q batch <<-EOF >/dev/null
			delete network.wg${interface}
			delete network.@wireguard_wg${interface}[0]
			delete network.ovpn${interface}
			delete openvpn.${interface}
			commit openvpn
			set openmptcprouter.${interface}.multipath="${multipath}"
			set network.${interface}.multipath="${multipath}"
			set openmptcprouter.${interface}.multipathvpn="0"
			delete openmptcprouter.ovpn${interface}
			delete openmptcprouter.wg${interface}
			commit openmptcprouter
			commit network
			del_list firewall.zone_vpn.network="ovpn${interface}"
			del_list firewall.zone_vpn.network="wg${interface}"
			commit firewall
		EOF
		need_reload=1
	elif [ "$(uci -q get openmptcprouter.${interface}.vpn)" = "1" ]; then
		intf="$(echo ${interface} | sed 's/ovpn//g')"
		[ "$intf" = "$interface" ] && intf="$(echo ${interface} | sed 's/wg//g')"
		if [ -n "$intf" ] && [ "$intf" != "$interface" ] && [ "$(uci -q get network.${intf})" = "" ]; then
			_tunnel_gone "${interface}"
			uci -q batch <<-EOF >/dev/null
				delete network.${interface}
				delete network.@wireguard_${interface}[0]
				delete openvpn.${intf}
				commit openvpn
				delete openmptcprouter.${intf}
				delete openmptcprouter.ovpn${intf}
				delete openmptcprouter.${interface}
				commit openmptcprouter
				commit network
				del_list firewall.zone_vpn.network="${interface}"
				commit firewall
			EOF
			need_reload=1
		fi
	fi
}

_master_ip() {
	[ -z "$remoteip" ] && [ "$(uci -q get openmptcprouter.$1.master)" = "1" ] && \
		remoteip=$(uci -q get openmptcprouter.$1.ip | awk '{print $1}')
}

# Back from MPTCP over VPN on every WAN: only what it changed is undone. The
# ss_rules of a proxy package that is not installed reads as empty, and that
# used to send every network reload here to point the VPNs at the first IP of
# the "vps" section, whatever server was in use.
mptcp_over_vpn_restore() {
	local restored="" ss vpnhosts vpnhost
	for ss in shadowsocks-libev shadowsocks-rust; do
		[ -n "$(uci -q get ${ss}.ss_rules)" ] || continue
		[ "$(uci -q get ${ss}.ss_rules.server)" != "sss0" ] || continue
		uci -q set ${ss}.ss_rules.server='sss0'
		uci -q commit ${ss}
		restored="1"
	done
	vpnhosts=" $(uci -q get glorytun.vpn.host) $(uci -q get glorytun-udp.vpn.host) $(uci -q get openvpn.omr.remote) "
	case "$vpnhosts" in
		*" 10.255.250.1 "*|*" 10.255.247.1 "*)
			remoteip=""
			config_load openmptcprouter
			config_foreach _getremoteip server
			config_foreach _master_ip server
			[ -n "$remoteip" ] || remoteip=$(uci -q get openmptcprouter.vps.ip | awk '{print $1}')
			if [ -n "$remoteip" ]; then
				for vpnhost in glorytun.vpn.host glorytun-udp.vpn.host; do
					case "$(uci -q get $vpnhost)" in
						10.255.250.1|10.255.247.1) uci -q set $vpnhost="$remoteip" ;;
					esac
				done
				case " $(uci -q get openvpn.omr.remote) " in
					*" 10.255.250.1 "*|*" 10.255.247.1 "*)
						uci -q batch <<-EOF >/dev/null
							delete openvpn.omr.remote
							add_list openvpn.omr.remote="$remoteip"
						EOF
					;;
				esac
				uci -q batch <<-EOF >/dev/null
					commit glorytun
					commit glorytun-udp
					commit openvpn
				EOF
				restored="1"
			fi
		;;
	esac
	[ "$restored" = "1" ] || return
	if [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then
		uci -q batch <<-EOF >/dev/null
			set shadowsocks-libev.sss0.disabled='0'
			commit shadowsocks-libev
		EOF
	elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks-rust" ]; then
		uci -q batch <<-EOF >/dev/null
			set shadowsocks-rust.sss0.disabled='0'
			commit shadowsocks-rust
		EOF
	fi
}

# The proxy and openvpn are restarted only when their config really changed:
# this runs on every network reload.
_config_sum() {
	md5sum /etc/config/$1 2>/dev/null
}

start_service()
{
	local libev_sum="$(_config_sum shadowsocks-libev)"
	local rust_sum="$(_config_sum shadowsocks-rust)"
	local openvpn_sum="$(_config_sum openvpn)"
	nbintf=0
	nbintfvpn=0
	need_reload=""
	gone_tunnels=""
	gone_lists=""
	wg_peers_server=""
	vpn="$(uci -q get openmptcprouter.settings.mptcpovervpn)"
	[ -z "$vpn" ] && vpn="openvpn"
	config_load openmptcprouter
	config_foreach mptcp_over_vpn interface
	if [ "$nbintf" = "$nbintfvpn" ] &&  [ "$nbintf" != "0" ] && [ "$nbintfvpn" != "0" ]; then
		uci -q batch <<-EOF >/dev/null
			set openmptcprouter.settings.allmptcpovervpn='1'
			commit openmptcprouter
		EOF
	else
		uci -q batch <<-EOF >/dev/null
			set openmptcprouter.settings.allmptcpovervpn='0'
			commit openmptcprouter
		EOF
	fi
	if [ "$nbintf" = "$nbintfvpn" ] &&  [ "$nbintf" != "0" ]; then
		if [ "$vpn" = "openvpn" ]; then
			if [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then
				uci -q batch <<-EOF >/dev/null
					set shadowsocks-libev.sss0.disabled='1'
				EOF
			elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks-rust" ]; then
				uci -q batch <<-EOF >/dev/null
					set shadowsocks-rust.sss0.disabled='1'
				EOF
			fi
			uci -q batch <<-EOF >/dev/null
				set glorytun.vpn.host='10.255.250.1'
				set glorytun-udp.vpn.host='10.255.250.1'
				del openvpn.omr.remote
				set openvpn.omr.remote='10.255.250.1'
				commit glorytun
				commit glorytun-udp
				commit openvpn
			EOF
		else
			if [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then
				uci -q batch <<-EOF >/dev/null
					set shadowsocks-libev.sss0.disabled='1'
				EOF
			elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks-rust" ]; then
				uci -q batch <<-EOF >/dev/null
					set shadowsocks-rust.sss0.disabled='1'
				EOF
			fi
			uci -q batch <<-EOF >/dev/null
				set glorytun.vpn.host='10.255.247.1'
				set glorytun-udp.vpn.host='10.255.247.1'
				del openvpn.omr.remote
				set openvpn.omr.remote='10.255.247.1'
				commit glorytun
				commit glorytun-udp
				commit openvpn
			EOF
		fi
		uci -q batch <<-EOF >/dev/null
			set shadowsocks-libev.ss_rules.server='mptcpovervpn'
			set shadowsocks-libev.ss_rules.redir_tcp='all'
			set shadowsocks-rust.ss_rules.server='mptcpovervpn'
			set shadowsocks-rust.ss_rules.redir_tcp='all'
		EOF
	elif [ "$nbintf" != "$nbintfvpn" ]; then
		mptcp_over_vpn_restore
	fi
	NBCPU=$(grep -c '^processor' /proc/cpuinfo | tr -d "\n")
	if [ "$nbintfvpn" != 0 ] && [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then
		if [ "$vpn" = "openvpn" ]; then
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-libev.mptcpovervpn=server
				set shadowsocks-libev.mptcpovervpn.server_port="$(uci -q get shadowsocks-libev.sss0.server_port)"
				set shadowsocks-libev.mptcpovervpn.key="$(uci -q get shadowsocks-libev.sss0.key)"
				set shadowsocks-libev.mptcpovervpn.method="$(uci -q get shadowsocks-libev.sss0.method)"
				set shadowsocks-libev.mptcpovervpn.server="10.255.250.1"
				set shadowsocks-libev.mptcpovervpn.disabled='0'
			EOF
		else
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-libev.mptcpovervpn=server
				set shadowsocks-libev.mptcpovervpn.server_port="$(uci -q get shadowsocks-libev.sss0.server_port)"
				set shadowsocks-libev.mptcpovervpn.key="$(uci -q get shadowsocks-libev.sss0.key)"
				set shadowsocks-libev.mptcpovervpn.method="$(uci -q get shadowsocks-libev.sss0.method)"
				set shadowsocks-libev.mptcpovervpn.server="10.255.247.1"
				set shadowsocks-libev.mptcpovervpn.disabled='0'
			EOF
		fi
		for c in $(seq 1 $NBCPU); do
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-libev.hivpn$c=ss_redir
				set shadowsocks-libev.hivpn$c.server="mptcpovervpn"
				set shadowsocks-libev.hivpn$c.local_address='0.0.0.0'
				set shadowsocks-libev.hivpn$c.local_port='1101'
				set shadowsocks-libev.hivpn$c.mode='tcp_and_udp'
				set shadowsocks-libev.hivpn$c.timeout='1000'
				set shadowsocks-libev.hivpn$c.fast_open='1'
				set shadowsocks-libev.hivpn$c.verbose='0'
				set shadowsocks-libev.hivpn$c.syslog='1'
				set shadowsocks-libev.hivpn$c.reuse_port='1'
				set shadowsocks-libev.hivpn$c.mptcp='1'
				set shadowsocks-libev.hivpn$c.ipv6_first='1'
				set shadowsocks-libev.hivpn$c.no_delay='1'
			EOF
		done
		uci -q batch <<-EOF >/dev/null
			commit shadowsocks-libev
		EOF
		[ "$(_config_sum shadowsocks-libev)" != "$libev_sum" ] && /etc/init.d/shadowsocks-libev restart
		[ "$(_config_sum openvpn)" != "$openvpn_sum" ] && /etc/init.d/openvpn restart && openvpn_sum="$(_config_sum openvpn)"
	elif [ "$nbintfvpn" != 0 ] && [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks-rust" ]; then
		if [ "$vpn" = "openvpn" ]; then
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-rust.mptcpovervpn=server
				set shadowsocks-rust.mptcpovervpn.server_port="$(uci -q get shadowsocks-rust.sss0.server_port)"
				set shadowsocks-rust.mptcpovervpn.password="$(uci -q get shadowsocks-rust.sss0.password)"
				set shadowsocks-rust.mptcpovervpn.method="$(uci -q get shadowsocks-rust.sss0.method)"
				set shadowsocks-rust.mptcpovervpn.server="10.255.250.1"
				set shadowsocks-rust.mptcpovervpn.disabled='0'
			EOF
		else
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-rust.mptcpovervpn=server
				set shadowsocks-rust.mptcpovervpn.server_port="$(uci -q get shadowsocks-rust.sss0.server_port)"
				set shadowsocks-rust.mptcpovervpn.password="$(uci -q get shadowsocks-rust.sss0.password)"
				set shadowsocks-rust.mptcpovervpn.method="$(uci -q get shadowsocks-rust.sss0.method)"
				set shadowsocks-rust.mptcpovervpn.server="10.255.247.1"
				set shadowsocks-rust.mptcpovervpn.disabled='0'
			EOF
		fi
		uci -q batch <<-EOF >/dev/null
			set shadowsocks-rust.hivpn=ss_redir
			set shadowsocks-rust.hivpn.server="mptcpovervpn"
			set shadowsocks-rust.hivpn.local_address='0.0.0.0'
			set shadowsocks-rust.hivpn.local_port='1101'
			set shadowsocks-rust.hivpn.mode='tcp_and_udp'
			set shadowsocks-rust.hivpn.timeout='1000'
			set shadowsocks-rust.hivpn.fast_open='1'
			set shadowsocks-rust.hivpn.verbose='0'
			set shadowsocks-rust.hivpn.syslog='1'
			set shadowsocks-rust.hivpn.reuse_port='1'
			set shadowsocks-rust.hivpn.mptcp='1'
			set shadowsocks-rust.hivpn.ipv6_first='1'
			set shadowsocks-rust.hivpn.no_delay='1'
		EOF
		uci -q batch <<-EOF >/dev/null
			commit shadowsocks-rust
		EOF
		[ "$(_config_sum shadowsocks-rust)" != "$rust_sum" ] && /etc/init.d/shadowsocks-rust restart
		[ "$(_config_sum openvpn)" != "$openvpn_sum" ] && /etc/init.d/openvpn restart && openvpn_sum="$(_config_sum openvpn)"
	elif [ "$(uci -q get shadowsocks-libev.hivpn1)" != "" ] || [ "$(uci -q get shadowsocks-rust.hivpn)" != "" ]; then
		# shadowsocks-rust has only hivpn: without shadowsocks-libev's, its
		# redir to the tunnel stayed and took part of the proxied traffic
		for c in $(seq 1 $NBCPU); do
			uci -q batch <<-EOF >/dev/null
				delete shadowsocks-libev.hivpn$c
			EOF
		done
		uci -q batch <<-EOF >/dev/null
			delete shadowsocks-rust.hivpn
		EOF
		if [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks" ]; then
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-libev.sss0.disabled='0'
			EOF
		elif [ "$(uci -q get openmptcprouter.settings.proxy)" = "shadowsocks-rust" ]; then
			uci -q batch <<-EOF >/dev/null
				set shadowsocks-rust.sss0.disabled='0'
			EOF
		fi
		uci -q batch <<-EOF >/dev/null
			set shadowsocks-libev.ss_rules.server='sss0'
			set shadowsocks-rust.ss_rules.server='sss0'
			delete shadowsocks-libev.mptcpovervpn
			commit shadowsocks-libev
			delete shadowsocks-rust.mptcpovervpn
			commit shadowsocks-rust
		EOF
		# the proxy in use only, as above: its redirs to the tunnel are gone
		case "$(uci -q get openmptcprouter.settings.proxy)" in
			shadowsocks) [ "$(_config_sum shadowsocks-libev)" != "$libev_sum" ] && /etc/init.d/shadowsocks-libev restart ;;
			shadowsocks-rust) [ "$(_config_sum shadowsocks-rust)" != "$rust_sum" ] && /etc/init.d/shadowsocks-rust restart ;;
		esac
	fi
	# The tunnels made, re-pointed or removed above were only committed:
	# a WAN back from MPTCP over VPN kept its wg<wan> up with its tracker,
	# routes and zone, and an OpenVPN one waited for an unrelated reload.
	# Once, after every WAN. None of these is a config.change of network,
	# this service's reload trigger.
	if [ "$need_reload" = "1" ]; then
		[ "$(_config_sum openvpn)" != "$openvpn_sum" ] && /etc/init.d/openvpn reload >/dev/null 2>&1
		ubus call network reload >/dev/null 2>&1
		/etc/init.d/firewall reload >/dev/null 2>&1
		_tunnels_cleanup
	fi
	# In the background: it asks the VPS API, and omr-tracker-server waits
	# for this restart on a failover
	[ -n "$wg_peers_server" ] && \
		( /etc/init.d/openmptcprouter-vps set_wireguard "$wg_peers_server" >/dev/null 2>&1 </dev/null & )
	if [ "$BOOT" = "1" ]; then
		config_load network
		config_foreach wireguard_restart interface
	fi

}

boot() {
	BOOT=1
	start
}

service_triggers() {
	procd_add_reload_trigger "network"
}