1#! /bin/bash
2# SPDX-License-Identifier: GPL-2.0
3
4readonly KSFT_PASS=0
5readonly KSFT_FAIL=1
6readonly KSFT_SKIP=4
7
8# shellcheck disable=SC2155 # declare and assign separately
9readonly KSFT_TEST="${MPTCP_LIB_KSFT_TEST:-$(basename "${0}" .sh)}"
10
11# These variables are used in some selftests, read-only
12declare -rx MPTCP_LIB_EVENT_CREATED=1           # MPTCP_EVENT_CREATED
13declare -rx MPTCP_LIB_EVENT_ESTABLISHED=2       # MPTCP_EVENT_ESTABLISHED
14declare -rx MPTCP_LIB_EVENT_CLOSED=3            # MPTCP_EVENT_CLOSED
15declare -rx MPTCP_LIB_EVENT_ANNOUNCED=6         # MPTCP_EVENT_ANNOUNCED
16declare -rx MPTCP_LIB_EVENT_REMOVED=7           # MPTCP_EVENT_REMOVED
17declare -rx MPTCP_LIB_EVENT_SUB_ESTABLISHED=10  # MPTCP_EVENT_SUB_ESTABLISHED
18declare -rx MPTCP_LIB_EVENT_SUB_CLOSED=11       # MPTCP_EVENT_SUB_CLOSED
19declare -rx MPTCP_LIB_EVENT_SUB_PRIORITY=13     # MPTCP_EVENT_SUB_PRIORITY
20declare -rx MPTCP_LIB_EVENT_LISTENER_CREATED=15 # MPTCP_EVENT_LISTENER_CREATED
21declare -rx MPTCP_LIB_EVENT_LISTENER_CLOSED=16  # MPTCP_EVENT_LISTENER_CLOSED
22
23declare -rx MPTCP_LIB_AF_INET=2
24declare -rx MPTCP_LIB_AF_INET6=10
25
26MPTCP_LIB_SUBTESTS=()
27
28# only if supported (or forced) and not disabled, see no-color.org
29if { [ -t 1 ] || [ "${SELFTESTS_MPTCP_LIB_COLOR_FORCE:-}" = "1" ]; } &&
30   [ "${NO_COLOR:-}" != "1" ]; then
31	readonly MPTCP_LIB_COLOR_RED="\E[1;31m"
32	readonly MPTCP_LIB_COLOR_GREEN="\E[1;32m"
33	readonly MPTCP_LIB_COLOR_YELLOW="\E[1;33m"
34	readonly MPTCP_LIB_COLOR_BLUE="\E[1;34m"
35	readonly MPTCP_LIB_COLOR_RESET="\E[0m"
36else
37	readonly MPTCP_LIB_COLOR_RED=
38	readonly MPTCP_LIB_COLOR_GREEN=
39	readonly MPTCP_LIB_COLOR_YELLOW=
40	readonly MPTCP_LIB_COLOR_BLUE=
41	readonly MPTCP_LIB_COLOR_RESET=
42fi
43
44# $1: color, $2: text
45mptcp_lib_print_color() {
46	echo -e "${MPTCP_LIB_START_PRINT:-}${*}${MPTCP_LIB_COLOR_RESET}"
47}
48
49mptcp_lib_print_ok() {
50	mptcp_lib_print_color "${MPTCP_LIB_COLOR_GREEN}${*}"
51}
52
53mptcp_lib_print_warn() {
54	mptcp_lib_print_color "${MPTCP_LIB_COLOR_YELLOW}${*}"
55}
56
57mptcp_lib_print_info() {
58	mptcp_lib_print_color "${MPTCP_LIB_COLOR_BLUE}${*}"
59}
60
61mptcp_lib_print_err() {
62	mptcp_lib_print_color "${MPTCP_LIB_COLOR_RED}${*}"
63}
64
65# SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var can be set when validating all
66# features using the last version of the kernel and the selftests to make sure
67# a test is not being skipped by mistake.
68mptcp_lib_expect_all_features() {
69	[ "${SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES:-}" = "1" ]
70}
71
72# $1: msg
73mptcp_lib_fail_if_expected_feature() {
74	if mptcp_lib_expect_all_features; then
75		echo "ERROR: missing feature: ${*}"
76		exit ${KSFT_FAIL}
77	fi
78
79	return 1
80}
81
82# $1: file
83mptcp_lib_has_file() {
84	local f="${1}"
85
86	if [ -f "${f}" ]; then
87		return 0
88	fi
89
90	mptcp_lib_fail_if_expected_feature "${f} file not found"
91}
92
93mptcp_lib_check_mptcp() {
94	if ! mptcp_lib_has_file "/proc/sys/net/mptcp/enabled"; then
95		echo "SKIP: MPTCP support is not available"
96		exit ${KSFT_SKIP}
97	fi
98}
99
100mptcp_lib_check_kallsyms() {
101	if ! mptcp_lib_has_file "/proc/kallsyms"; then
102		echo "SKIP: CONFIG_KALLSYMS is missing"
103		exit ${KSFT_SKIP}
104	fi
105}
106
107# Internal: use mptcp_lib_kallsyms_has() instead
108__mptcp_lib_kallsyms_has() {
109	local sym="${1}"
110
111	mptcp_lib_check_kallsyms
112
113	grep -q " ${sym}" /proc/kallsyms
114}
115
116# $1: part of a symbol to look at, add '$' at the end for full name
117mptcp_lib_kallsyms_has() {
118	local sym="${1}"
119
120	if __mptcp_lib_kallsyms_has "${sym}"; then
121		return 0
122	fi
123
124	mptcp_lib_fail_if_expected_feature "${sym} symbol not found"
125}
126
127# $1: part of a symbol to look at, add '$' at the end for full name
128mptcp_lib_kallsyms_doesnt_have() {
129	local sym="${1}"
130
131	if ! __mptcp_lib_kallsyms_has "${sym}"; then
132		return 0
133	fi
134
135	mptcp_lib_fail_if_expected_feature "${sym} symbol has been found"
136}
137
138# !!!AVOID USING THIS!!!
139# Features might not land in the expected version and features can be backported
140#
141# $1: kernel version, e.g. 6.3
142mptcp_lib_kversion_ge() {
143	local exp_maj="${1%.*}"
144	local exp_min="${1#*.}"
145	local v maj min
146
147	# If the kernel has backported features, set this env var to 1:
148	if [ "${SELFTESTS_MPTCP_LIB_NO_KVERSION_CHECK:-}" = "1" ]; then
149		return 0
150	fi
151
152	v=$(uname -r | cut -d'.' -f1,2)
153	maj=${v%.*}
154	min=${v#*.}
155
156	if   [ "${maj}" -gt "${exp_maj}" ] ||
157	   { [ "${maj}" -eq "${exp_maj}" ] && [ "${min}" -ge "${exp_min}" ]; }; then
158		return 0
159	fi
160
161	mptcp_lib_fail_if_expected_feature "kernel version ${1} lower than ${v}"
162}
163
164__mptcp_lib_result_add() {
165	local result="${1}"
166	shift
167
168	local id=$((${#MPTCP_LIB_SUBTESTS[@]} + 1))
169
170	MPTCP_LIB_SUBTESTS+=("${result} ${id} - ${KSFT_TEST}: ${*}")
171}
172
173# $1: test name
174mptcp_lib_result_pass() {
175	__mptcp_lib_result_add "ok" "${1}"
176}
177
178# $1: test name
179mptcp_lib_result_fail() {
180	__mptcp_lib_result_add "not ok" "${1}"
181}
182
183# $1: test name
184mptcp_lib_result_skip() {
185	__mptcp_lib_result_add "ok" "${1} # SKIP"
186}
187
188# $1: result code ; $2: test name
189mptcp_lib_result_code() {
190	local ret="${1}"
191	local name="${2}"
192
193	case "${ret}" in
194		"${KSFT_PASS}")
195			mptcp_lib_result_pass "${name}"
196			;;
197		"${KSFT_FAIL}")
198			mptcp_lib_result_fail "${name}"
199			;;
200		"${KSFT_SKIP}")
201			mptcp_lib_result_skip "${name}"
202			;;
203		*)
204			echo "ERROR: wrong result code: ${ret}"
205			exit ${KSFT_FAIL}
206			;;
207	esac
208}
209
210mptcp_lib_result_print_all_tap() {
211	local subtest
212
213	if [ ${#MPTCP_LIB_SUBTESTS[@]} -eq 0 ] ||
214	   [ "${SELFTESTS_MPTCP_LIB_NO_TAP:-}" = "1" ]; then
215		return
216	fi
217
218	printf "\nTAP version 13\n"
219	printf "1..%d\n" "${#MPTCP_LIB_SUBTESTS[@]}"
220
221	for subtest in "${MPTCP_LIB_SUBTESTS[@]}"; do
222		printf "%s\n" "${subtest}"
223	done
224}
225
226# get the value of keyword $1 in the line marked by keyword $2
227mptcp_lib_get_info_value() {
228	grep "${2}" | sed -n 's/.*\('"${1}"':\)\([0-9a-f:.]*\).*$/\2/p;q'
229}
230
231# $1: info name ; $2: evts_ns ; [$3: event type; [$4: addr]]
232mptcp_lib_evts_get_info() {
233	grep "${4:-}" "${2}" | mptcp_lib_get_info_value "${1}" "^type:${3:-1},"
234}
235
236# $1: PID
237mptcp_lib_kill_wait() {
238	[ "${1}" -eq 0 ] && return 0
239
240	kill -SIGUSR1 "${1}" > /dev/null 2>&1
241	kill "${1}" > /dev/null 2>&1
242	wait "${1}" 2>/dev/null
243}
244
245# $1: IP address
246mptcp_lib_is_v6() {
247	[ -z "${1##*:*}" ]
248}
249
250# $1: ns, $2: MIB counter
251mptcp_lib_get_counter() {
252	local ns="${1}"
253	local counter="${2}"
254	local count
255
256	count=$(ip netns exec "${ns}" nstat -asz "${counter}" |
257		awk 'NR==1 {next} {print $2}')
258	if [ -z "${count}" ]; then
259		mptcp_lib_fail_if_expected_feature "${counter} counter"
260		return 1
261	fi
262
263	echo "${count}"
264}
265
266mptcp_lib_events() {
267	local ns="${1}"
268	local evts="${2}"
269	declare -n pid="${3}"
270
271	:>"${evts}"
272
273	mptcp_lib_kill_wait "${pid:-0}"
274	ip netns exec "${ns}" ./pm_nl_ctl events >> "${evts}" 2>&1 &
275	pid=$!
276}
277