File: //usr/lib/modules/5.15.0-153-generic/build/tools/testing/selftests/net/vrf_strict_mode_test.sh
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# This test is designed for testing the new VRF strict_mode functionality.
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
ret=0
# identifies the "init" network namespace which is often called root network
# namespace.
INIT_NETNS_NAME="init"
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
log_test()
{
	local rc=$1
	local expected=$2
	local msg="$3"
	if [ ${rc} -eq ${expected} ]; then
		nsuccess=$((nsuccess+1))
		printf "\n    TEST: %-60s  [ OK ]\n" "${msg}"
	else
		ret=1
		nfail=$((nfail+1))
		printf "\n    TEST: %-60s  [FAIL]\n" "${msg}"
		if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
			echo
			echo "hit enter to continue, 'q' to quit"
			read a
			[ "$a" = "q" ] && exit 1
		fi
	fi
}
print_log_test_results()
{
	if [ "$TESTS" != "none" ]; then
		printf "\nTests passed: %3d\n" ${nsuccess}
		printf "Tests failed: %3d\n"   ${nfail}
	fi
}
log_section()
{
	echo
	echo "################################################################################"
	echo "TEST SECTION: $*"
	echo "################################################################################"
}
ip_expand_args()
{
	local nsname=$1
	local nsarg=""
	if [ "${nsname}" != "${INIT_NETNS_NAME}" ]; then
		nsarg="-netns ${nsname}"
	fi
	echo "${nsarg}"
}
vrf_count()
{
	local nsname=$1
	local nsarg="$(ip_expand_args ${nsname})"
	ip ${nsarg} -o link show type vrf | wc -l
}
count_vrf_by_table_id()
{
	local nsname=$1
	local tableid=$2
	local nsarg="$(ip_expand_args ${nsname})"
	ip ${nsarg} -d -o link show type vrf | grep "table ${tableid}" | wc -l
}
add_vrf()
{
	local nsname=$1
	local vrfname=$2
	local vrftable=$3
	local nsarg="$(ip_expand_args ${nsname})"
	ip ${nsarg} link add ${vrfname} type vrf table ${vrftable} &>/dev/null
}
add_vrf_and_check()
{
	local nsname=$1
	local vrfname=$2
	local vrftable=$3
	local cnt
	local rc
	add_vrf ${nsname} ${vrfname} ${vrftable}; rc=$?
	cnt=$(count_vrf_by_table_id ${nsname} ${vrftable})
	log_test ${rc} 0 "${nsname}: add vrf ${vrfname}, ${cnt} vrfs for table ${vrftable}"
}
add_vrf_and_check_fail()
{
	local nsname=$1
	local vrfname=$2
	local vrftable=$3
	local cnt
	local rc
	add_vrf ${nsname} ${vrfname} ${vrftable}; rc=$?
	cnt=$(count_vrf_by_table_id ${nsname} ${vrftable})
	log_test ${rc} 2 "${nsname}: CANNOT add vrf ${vrfname}, ${cnt} vrfs for table ${vrftable}"
}
del_vrf_and_check()
{
	local nsname=$1
	local vrfname=$2
	local nsarg="$(ip_expand_args ${nsname})"
	ip ${nsarg} link del ${vrfname}
	log_test $? 0 "${nsname}: remove vrf ${vrfname}"
}
config_vrf_and_check()
{
	local nsname=$1
	local addr=$2
	local vrfname=$3
	local nsarg="$(ip_expand_args ${nsname})"
	ip ${nsarg} link set dev ${vrfname} up && \
		ip ${nsarg} addr add ${addr} dev ${vrfname}
	log_test $? 0 "${nsname}: vrf ${vrfname} up, addr ${addr}"
}
read_strict_mode()
{
	local nsname=$1
	local rval
	local rc=0
	local nsexec=""
	if [ "${nsname}" != "${INIT_NETNS_NAME}" ]; then
		# a custom network namespace is provided
		nsexec="ip netns exec ${nsname}"
	fi
	rval="$(${nsexec} bash -c "cat /proc/sys/net/vrf/strict_mode" | \
		grep -E "^[0-1]$")" &> /dev/null
	if [ $? -ne 0 ]; then
		# set errors
		rval=255
		rc=1
	fi
	# on success, rval can be only 0 or 1; on error, rval is equal to 255
	echo ${rval}
	return ${rc}
}
read_strict_mode_compare_and_check()
{
	local nsname=$1
	local expected=$2
	local res
	res="$(read_strict_mode ${nsname})"
	log_test ${res} ${expected} "${nsname}: check strict_mode=${res}"
}
set_strict_mode()
{
	local nsname=$1
	local val=$2
	local nsexec=""
	if [ "${nsname}" != "${INIT_NETNS_NAME}" ]; then
		# a custom network namespace is provided
		nsexec="ip netns exec ${nsname}"
	fi
	${nsexec} bash -c "echo ${val} >/proc/sys/net/vrf/strict_mode" &>/dev/null
}
enable_strict_mode()
{
	local nsname=$1
	set_strict_mode ${nsname} 1
}
disable_strict_mode()
{
	local nsname=$1
	set_strict_mode ${nsname} 0
}
disable_strict_mode_and_check()
{
	local nsname=$1
	disable_strict_mode ${nsname}
	log_test $? 0 "${nsname}: disable strict_mode (=0)"
}
enable_strict_mode_and_check()
{
	local nsname=$1
	enable_strict_mode ${nsname}
	log_test $? 0 "${nsname}: enable strict_mode (=1)"
}
enable_strict_mode_and_check_fail()
{
	local nsname=$1
	enable_strict_mode ${nsname}
	log_test $? 1 "${nsname}: CANNOT enable strict_mode"
}
strict_mode_check_default()
{
	local nsname=$1
	local strictmode
	local vrfcnt
	vrfcnt=$(vrf_count ${nsname})
	strictmode=$(read_strict_mode ${nsname})
	log_test ${strictmode} 0 "${nsname}: strict_mode=0 by default, ${vrfcnt} vrfs"
}
setup()
{
	modprobe vrf
	ip netns add testns
	ip netns exec testns ip link set lo up
}
cleanup()
{
	ip netns del testns 2>/dev/null
	ip link del vrf100 2>/dev/null
	ip link del vrf101 2>/dev/null
	ip link del vrf102 2>/dev/null
	echo 0 >/proc/sys/net/vrf/strict_mode 2>/dev/null
}
vrf_strict_mode_tests_init()
{
	vrf_strict_mode_check_support init
	strict_mode_check_default init
	add_vrf_and_check init vrf100 100
	config_vrf_and_check init 172.16.100.1/24 vrf100
	enable_strict_mode_and_check init
	add_vrf_and_check_fail init vrf101 100
	disable_strict_mode_and_check init
	add_vrf_and_check init vrf101 100
	config_vrf_and_check init 172.16.101.1/24 vrf101
	enable_strict_mode_and_check_fail init
	del_vrf_and_check init vrf101
	enable_strict_mode_and_check init
	add_vrf_and_check init vrf102 102
	config_vrf_and_check init 172.16.102.1/24 vrf102
	# the strict_modle is enabled in the init
}
vrf_strict_mode_tests_testns()
{
	vrf_strict_mode_check_support testns
	strict_mode_check_default testns
	enable_strict_mode_and_check testns
	add_vrf_and_check testns vrf100 100
	config_vrf_and_check testns 10.0.100.1/24 vrf100
	add_vrf_and_check_fail testns vrf101 100
	add_vrf_and_check_fail testns vrf102 100
	add_vrf_and_check testns vrf200 200
	disable_strict_mode_and_check testns
	add_vrf_and_check testns vrf101 100
	add_vrf_and_check testns vrf102 100
	#the strict_mode is disabled in the testns
}
vrf_strict_mode_tests_mix()
{
	read_strict_mode_compare_and_check init 1
	read_strict_mode_compare_and_check testns 0
	del_vrf_and_check testns vrf101
	del_vrf_and_check testns vrf102
	disable_strict_mode_and_check init
	enable_strict_mode_and_check testns
	enable_strict_mode_and_check init
	enable_strict_mode_and_check init
	disable_strict_mode_and_check testns
	disable_strict_mode_and_check testns
	read_strict_mode_compare_and_check init 1
	read_strict_mode_compare_and_check testns 0
}
vrf_strict_mode_tests()
{
	log_section "VRF strict_mode test on init network namespace"
	vrf_strict_mode_tests_init
	log_section "VRF strict_mode test on testns network namespace"
	vrf_strict_mode_tests_testns
	log_section "VRF strict_mode test mixing init and testns network namespaces"
	vrf_strict_mode_tests_mix
}
vrf_strict_mode_check_support()
{
	local nsname=$1
	local output
	local rc
	output="$(lsmod | grep '^vrf' | awk '{print $1}')"
	if [ -z "${output}" ]; then
		modinfo vrf || return $?
	fi
	# we do not care about the value of the strict_mode; we only check if
	# the strict_mode parameter is available or not.
	read_strict_mode ${nsname} &>/dev/null; rc=$?
	log_test ${rc} 0 "${nsname}: net.vrf.strict_mode is available"
	return ${rc}
}
if [ "$(id -u)" -ne 0 ];then
	echo "SKIP: Need root privileges"
	exit $ksft_skip
fi
if [ ! -x "$(command -v ip)" ]; then
	echo "SKIP: Could not run test without ip tool"
	exit $ksft_skip
fi
modprobe vrf &>/dev/null
if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
	echo "SKIP: vrf sysctl does not exist"
	exit $ksft_skip
fi
cleanup &> /dev/null
setup
vrf_strict_mode_tests
cleanup
print_log_test_results
exit $ret