#!/bin/sh
# Copyright (C) 2022 iopsys Software Solutions AB
# Author: AMIN Ben Romdhane <amin.benromdhane@iopsys.eu>

. /usr/share/libubox/jshn.sh

ROOT="$(dirname "${0}")"
. "${ROOT}"/bbf_api

get_traceroute_log_file() {
	IDX=1
	LOG_FILE="/tmp/traceroute_$IDX.log"
	
	while [ -e ${LOG_FILE} ]; do
		IDX=$((IDX+1))
		LOG_FILE="/tmp/traceroute_$IDX.log";
	done

	echo ${LOG_FILE}
}

traceroute_error() {
	json_init
	json_add_string "Status" "$1"
	json_add_string "IPAddressUsed" "$2"
	json_add_int "ResponseTime" "0"
	json_dump

	# Store data in dmmap_diagnostics for both protocols (cwmp/usp)
	[ "$3" = "both_proto" ] && {
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.DiagnosticState="$1"
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.IPAddressUsed="$2"
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.ResponseTime=0
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.NumberOfHops=0

		$UCI_COMMIT_BBF_DMMAP
	}
}

traceroute_launch() {
	input="$1"

	json_load "${input}"
	
	json_get_var host host
	json_get_var iface iface
	json_get_var ip_proto ip_proto
	json_get_var cnt nbr_of_tries
	json_get_var timeout timeout
	json_get_var dsize data_size
	json_get_var dscp dscp
	json_get_var maxhop max_hop_cnt
	json_get_var proto proto
	json_get_var cancel cancel

	# Clear all route hops instances
	[ "$proto" = "both_proto" ] && {
		if [ -f "/tmp/traceroute_pid" ]; then
			old_pid=$(cat /tmp/traceroute_pid)
		fi

		[ -n "${old_pid}" ] && {
			if [ -f "/proc/$old_pid/cmdline" ]; then
				cmd=$(cat /proc/"${old_pid}"/cmdline)
			fi
		}

		if [[ "${cmd}" = *traceroute* ]]; then
			kill -9 "${old_pid}"
		fi

		res=$($UCI_SHOW_BBF_DMMAP dmmap_diagnostics | grep -E "=RouteHops$" | cut -d= -f 1)
		for i in $res; do
			$UCI_DELETE_BBF_DMMAP "${i}"
			$UCI_COMMIT_BBF_DMMAP
		done

		if [ "${cancel}" = "1" ]; then
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.DiagnosticState="None"
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.IPAddressUsed=""
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.NumberOfHops="0"
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.ResponseTime="0"
			$UCI_COMMIT_BBF_DMMAP

			json_init
			json_add_string "Status" "None"
			json_add_string "IPAddressUsed" ""
			json_add_int "ResponseTime" "0"
			json_dump

			return
		else
			echo $$ > /tmp/traceroute_pid
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.DiagnosticState="Requested_running"
			$UCI_COMMIT_BBF_DMMAP
		fi
	}

	LOG_FILE=$(get_traceroute_log_file)

	# Fail if host is empty
	[ -z "${host}" ] && {
		traceroute_error "Error_Internal" "" "${proto}"
		return
	}	

	ip_addr_used=$(get_ip_addr_used "${host}" "${ip_proto}" "${iface}")
	if [ -n "${iface}" ]; then
		device=$(ifstatus "${iface}" | jsonfilter -e '@.l3_device')

		# If no device was found, return error
		[ -z "${device}" ] && {
			traceroute_error "Error_NoRouteToHost" "${ip_addr_used}" "${proto}"
			return
		}
	else
		device=""
	fi	

	# Assign default value
	[ -z "${cnt}" ] && cnt=3
	[ -z "${maxhop}" ] && maxhop=30
	if [ "$ip_proto" = "IPv4" ]; then ip_proto="-4"; elif [ "$ip_proto" = "IPv6" ]; then ip_proto="-6"; else ip_proto=""; fi
	[ -z "${timeout}" ] && timeout=5 || timeout=$((timeout/1000))
	[ "${timeout}" = "0" ] && timeout=1
	[ -z "${dscp}" ] && dscp=0	
	[ -z "${dsize}" ] && dsize="38"

	if [ -z "${ip_proto}" ]; then
		if [ -z "${device}" ]; then
			res=$(traceroute -m "${maxhop}" -t "${dscp}" -w "${timeout}" -q "${cnt}" "${host}" "${dsize}" 2>&1 >"${LOG_FILE}")
		else
			res=$(traceroute -m "${maxhop}" -t "${dscp}" -w "${timeout}" -q "${cnt}" -i "${device}" "${host}" "${dsize}" 2>&1 >"${LOG_FILE}")
		fi
	else
		if [ -z "${device}" ]; then
			res=$(traceroute -m "${maxhop}" -t "${dscp}" -w "${timeout}" -q "${cnt}" "${ip_proto}" "${host}" "${dsize}" 2>&1 >"${LOG_FILE}")
		else
			res=$(traceroute -m "${maxhop}" -t "${dscp}" -w "${timeout}" -q "${cnt}" "${ip_proto}" -i "${device}" "${host}" "${dsize}" 2>&1 >"${LOG_FILE}")
		fi
	fi
	error_code="$?"

	ba=$(echo "$res" | grep "bad address")
	[ -n "$ba" ] && {
		traceroute_error "Error_CannotResolveHostName" "${ip_addr_used}" "${proto}"
		return
	}

	[ "$error_code" != "0" ] && {
		traceroute_error "Error_Other" "${ip_addr_used}" "${proto}"
		return
	}

	i=-2
	resp_time=0
	json_init
	json_add_array "RouteHops"

	while read -r _ host ip time _; do
		[ "$host" = "*" ] || [ "$ip" = "*" ] && continue
		i=$((i+1))
		[ "$i" = "-1" ] && continue;
		ip=${ip#(}; ip=${ip%)}
		time=${time%.*}
		[ "${time}" = "0" ] && time=1
		resp_time="${time}"

		json_add_object ""
		json_add_string "Host" "${host}"
		json_add_string "HostAddress" "${ip}"
		json_add_int "RTTimes" "${time}"
		json_add_int "ErrorCode" "0"
		json_close_object

		# Store data in dmmap_diagnostics for both protocols (cwmp/usp)
		[ "$proto" = "both_proto" ] && {
			sec_name=$($UCI_ADD_BBF_DMMAP dmmap_diagnostics RouteHops)
			$UCI_RENAME_BBF_DMMAP dmmap_diagnostics."${sec_name}"="route_hops_${i}"
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.route_hops_${i}.host="${host}"
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.route_hops_${i}.ip="${ip}"
			$UCI_SET_BBF_DMMAP dmmap_diagnostics.route_hops_${i}.time="${time}"
		}
			
	done < "${LOG_FILE}"

	rm -f "${LOG_FILE}"
	i=$((i+1))

	json_close_array
	json_add_string "Status" "Complete"
	json_add_string "IPAddressUsed" "${ip_addr_used}"
	json_add_int "ResponseTime" "${resp_time}"
	json_dump

	# Store data in dmmap_diagnostics for both protocols (cwmp/usp)
	[ "$proto" = "both_proto" ] && {
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.DiagnosticState="Complete"
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.IPAddressUsed="${ip_addr_used}"
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.NumberOfHops="${i}"
		$UCI_SET_BBF_DMMAP dmmap_diagnostics.traceroute.ResponseTime="${resp_time}"
		$UCI_COMMIT_BBF_DMMAP
	}
}

if [ -n "$1" ]; then
	traceroute_launch "$1"
else
	traceroute_error "Error_Internal"
fi
