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