#!/bin/sh

set -eu

# A stateful mock of ip(8) for the ikvswitch unit tests: successful
# mutations are appended to ${IKV_NET_MOCK_LOG} (queries and failures
# are not), and the state of links, addresses and routes is derived
# from that log, mimicking the kernel: creating an existing link
# fails, and deleting or showing a missing one fails too. The log is
# cumulative across runs, so the mock knows what a previous run
# created. Requires IKV_NET_MOCK_LOG to be set.

: "${IKV_NET_MOCK_LOG:?IKV_NET_MOCK_LOG must be set}"

link_created () {
	awk -v NAME="${1}" '
		$1 == "tuntap" && $2 == "add" && $4 == NAME { c++ }
		$1 == "tuntap" && $2 == "delete" && $5 == NAME { d++ }
		$1 == "link" && $2 == "add" && $3 == NAME { c++ }
		$1 == "link" && $2 == "delete" && $3 == NAME { d++ }
		$1 == "addbr" && $2 == NAME { c++ }
		$1 == "delbr" && $2 == NAME { d++ }
		END { exit !(c > d) }
	' "${IKV_NET_MOCK_LOG}"
}

case "${1}" in
link)
	case "${2}" in
	show)
		# Pure query: not logged.
		if ! link_created "${3}" ; then
			echo "Device \"${3}\" does not exist." >&2
			exit 1
		fi
		exit 0
		;;
	add)
		if link_created "${3}" ; then
			echo "RTNETLINK answers: File exists" >&2
			exit 2
		fi
		;;
	delete)
		if ! link_created "${3}" ; then
			echo "Cannot find device \"${3}\"" >&2
			exit 1
		fi
		;;
	set)
		if [ "${4}" = "dev" ] ; then
			NAME=${5}
		else
			NAME=${4}
		fi
		if ! link_created "${NAME}" ; then
			echo "Cannot find device \"${NAME}\"" >&2
			exit 1
		fi
		;;
	esac
	;;
tuntap)
	case "${2}" in
	add)
		if link_created "${4}" ; then
			echo "Create link failed: File exists" >&2
			exit 2
		fi
		;;
	delete)
		# Form used by ikvswitch-host-networking:
		# "tuntap delete mode tap <name>".
		if ! link_created "${5}" ; then
			echo "Cannot find device \"${5}\"" >&2
			exit 1
		fi
		;;
	esac
	;;
addr)
	case "${2}" in
	replace)
		# Idempotent by design, but the device must exist.
		if ! link_created "${5}" ; then
			echo "Cannot find device \"${5}\"" >&2
			exit 1
		fi
		;;
	add)
		if ! link_created "${5}" ; then
			echo "Cannot find device \"${5}\"" >&2
			exit 1
		fi
		;;
	del|delete)
		if ! link_created "${5}" ; then
			echo "Cannot find device \"${5}\"" >&2
			exit 1
		fi
		;;
	esac
	;;
route)
	# "route replace" is idempotent by design.
	;;
esac

echo "$@" >>"${IKV_NET_MOCK_LOG}"
exit 0
