#!/bin/sh

. /lib/functions.sh
. /lib/functions/network.sh
. /lib/mwan3/mwan3.sh

trap_with_arg()
{
	func="$1" ; shift
	pid="$1" ; shift
	for sig ; do
		# shellcheck disable=SC2064
		trap "$func $sig $pid" "$sig"
	done
}

func_trap()
{
	kill -${1} ${2} 2>/dev/null
}

mwan3_add_all_routes()
{
	local tid IP IPT route_line family active_tbls tid initial_state error
	local ipv=$1

	add_active_tbls()
	{
		let tid++
		config_get family "$1" family ipv4
		config_get initial_state "$1" initial_state "online"
		[ "$family" != "$ipv" ] && return
		if $IPT -S "mwan3_iface_in_$1" &> /dev/null; then
			active_tbls="$active_tbls${tid} "
		fi
	}

	add_route()
	{
		let tid++
		[ -n "${active_tbls##* $tid *}" ] && return
		error=$($IP route add table $tid $route_line 2>&1) ||
			LOG warn "failed to add $route_line to table $tid - error: $error"
	}

	mwan3_update_dev_to_table
	[ "$ipv" = "ipv6" ] && [ $NO_IPV6 -ne 0 ] && return
	if [ "$ipv" = "ipv4" ]; then
		IP="$IP4"
		IPT="$IPT4"
	elif [ "$ipv" = "ipv6" ]; then
		IP="$IP6"
		IPT="$IPT6"
	fi
	tid=0
	active_tbls=" "
	config_foreach add_active_tbls interface
	[ "$active_tbls" = " " ] && return
	mwan3_get_routes | while read -r route_line; do
		mwan3_route_line_dev "tid" "$route_line" "$ipv"
		if [ -n "$tid" ] && [ -z "${active_tbls##* $tid *}" ]; then
			$IP route add table $tid $route_line
		elif [ -n "${route_line##default*}" ] && [ -n "${route_line##fe80::/64*}" ]; then
			config_foreach add_route interface
		fi
	done
}

mwan3_rtmon_route_handle()
{
	local action route_line family tbl device line tid

	route_line=${1##"Deleted "}
	route_family=$2

	if [ "$route_line" = "$1" ]; then
		action="replace"
		$IPS -! add mwan3_connected_${route_family} ${route_line%% *}
	else
		action="del"
		mwan3_set_connected_${route_family}
	fi

	if [ -z "${route_line##*linkdown*}" ]; then
		LOG debug "attempting to add link on down interface - $route_line"
	fi

	if [ "$route_family" = "ipv4" ]; then
		IP="$IP4"
	elif [ "$route_family" = "ipv6" ] && [ $NO_IPV6 -eq 0 ]; then
		IP="$IP6"
	else
		LOG warn "route update called with invalid family - $route_family"
		return
	fi
	route_line=$(echo "$route_line" | sed -ne "$MWAN3_ROUTE_LINE_EXP")

	if [ "$action" = "del" ]; then
		if mwan3_get_routes | grep -qxF "$route_line"; then
			LOG debug "deleted but route still exists - $route_line"
			return
		fi
	fi

	handle_route() {
		local error
		local iface=$1
		tbl=$($IP route list table $tid 2>/dev/null)$'\n'

		local status
		mwan3_get_mwan3track_status status $iface

		if [ -n "$iface" ] && [ "$status" != "active" ]; then
			LOG debug "interface $iface is disabled - skipping '$route_line'";
			return
		fi

		# check that action needs to be performed. May not need to take action if we
		# got a delete event, but table was already flushed
		if [ $action = "del" ] && [ -n "${tbl##*$route_line$'\n'*}" ]; then
			LOG debug "skipping already deleted route table $tid - skipping '$route_line'"
			return
		fi

		network_get_device device "$iface"
		LOG debug "adjusting route $device: '$IP route $action table $tid $route_line'"
		error=$($IP route "$action" table $tid $route_line 2>&1)||
			LOG warn "failed: '$IP route $action table $tid $route_line' - error: $error"
	}
	handle_route_cb(){
		local iface=$1
		let tid++
		config_get family "$iface" family ipv4
		[ "$family" != "$route_family" ] && return
		handle_route "$iface"
	}

	mwan3_update_dev_to_table
	mwan3_route_line_dev "tid" "$route_line" "$route_family"

	if [ -n "$tid" ]; then
		handle_route
	elif [ -n "${route_line##default*}" ] && [ -n "${route_line##fe80::/64*}" ]; then
		config_foreach handle_route_cb interface
	fi
}

main()
{
	local IP family

	mwan3_init

	family=$1
	[ -z $family ] && family=ipv4
	if [ "$family" = "ipv6" ]; then
		if [ $NO_IPV6 -ne 0 ]; then
			LOG warn "mwan3rtmon started for ipv6, but ipv6 not enabled on system"
			exit 1
		fi
		IP="$IP6"
	else
		IP="$IP4"
	fi
	sh -c "echo \$\$; exec $IP monitor route" | {
		read -r monitor_pid
		trap_with_arg func_trap "$monitor_pid" SIGINT SIGTERM SIGKILL
		KILL -SIGSTOP $$
		while IFS='' read -r line; do
			[ -z "${line##*table*}" ] && continue
			LOG debug "handling route update $family '$line'"
			mwan3_rtmon_route_handle "$line" "$family"
		done
	} &
	child=$!
	trap_with_arg func_trap "$child" SIGINT SIGTERM SIGKILL
	mwan3_set_connected_${family}
	mwan3_add_all_routes ${family}
	kill -SIGCONT $child
	wait $child
}
main "$@"
