xref: /linux/tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh (revision 8bf22c33e7a172fbc72464f4cc484d23a6b412ba)
1#!/usr/bin/env bash
2# SPDX-License-Identifier: GPL-2.0
3
4# This file contains functions and helpers to support the netconsole
5# selftests
6#
7# Author: Breno Leitao <leitao@debian.org>
8
9set -euo pipefail
10
11LIBDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
12
13SRCIF="" # to be populated later
14SRCIP="" # to be populated later
15SRCIP4="192.0.2.1"
16SRCIP6="fc00::1"
17DSTIF="" # to be populated later
18DSTIP="" # to be populated later
19DSTIP4="192.0.2.2"
20DSTIP6="fc00::2"
21
22PORT="6666"
23MSG="netconsole selftest"
24USERDATA_KEY="key"
25USERDATA_VALUE="value"
26TARGET=$(mktemp -u netcons_XXXXX)
27DEFAULT_PRINTK_VALUES=$(cat /proc/sys/kernel/printk)
28NETCONS_CONFIGFS="/sys/kernel/config/netconsole"
29NETCONS_PATH="${NETCONS_CONFIGFS}"/"${TARGET}"
30# NAMESPACE will be populated by setup_ns with a random value
31NAMESPACE=""
32
33# IDs for netdevsim. We either use NSIM_DEV_{1,2}_ID for standard test
34# or NSIM_BOND_{T,R}X_{1,2} for the bonding tests. Not both at the
35# same time.
36NSIM_DEV_1_ID=$((256 + RANDOM % 256))
37NSIM_DEV_2_ID=$((512 + RANDOM % 256))
38NSIM_BOND_TX_1=$((768 + RANDOM % 256))
39NSIM_BOND_TX_2=$((1024 + RANDOM % 256))
40NSIM_BOND_RX_1=$((1280 + RANDOM % 256))
41NSIM_BOND_RX_2=$((1536 + RANDOM % 256))
42NSIM_DEV_SYS_NEW="/sys/bus/netdevsim/new_device"
43NSIM_DEV_SYS_LINK="/sys/bus/netdevsim/link_device"
44
45# Used to create and delete namespaces
46source "${LIBDIR}"/../../../../net/lib.sh
47
48# Create netdevsim interfaces
49create_ifaces() {
50	echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_NEW"
51	echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_NEW"
52	udevadm settle 2> /dev/null || true
53
54	local NSIM1=/sys/bus/netdevsim/devices/netdevsim"$NSIM_DEV_1_ID"
55	local NSIM2=/sys/bus/netdevsim/devices/netdevsim"$NSIM_DEV_2_ID"
56
57	# These are global variables
58	SRCIF=$(find "$NSIM1"/net -maxdepth 1 -type d ! \
59		-path "$NSIM1"/net -exec basename {} \;)
60	DSTIF=$(find "$NSIM2"/net -maxdepth 1 -type d ! \
61		-path "$NSIM2"/net -exec basename {} \;)
62}
63
64link_ifaces() {
65	local NSIM_DEV_SYS_LINK="/sys/bus/netdevsim/link_device"
66	local SRCIF_IFIDX=$(cat /sys/class/net/"$SRCIF"/ifindex)
67	local DSTIF_IFIDX=$(cat /sys/class/net/"$DSTIF"/ifindex)
68
69	exec {NAMESPACE_FD}</var/run/netns/"${NAMESPACE}"
70	exec {INITNS_FD}</proc/self/ns/net
71
72	# Bind the dst interface to namespace
73	ip link set "${DSTIF}" netns "${NAMESPACE}"
74
75	# Linking one device to the other one (on the other namespace}
76	if ! echo "${INITNS_FD}:$SRCIF_IFIDX $NAMESPACE_FD:$DSTIF_IFIDX"  > $NSIM_DEV_SYS_LINK
77	then
78		echo "linking netdevsim1 with netdevsim2 should succeed"
79		cleanup
80		exit "${ksft_skip}"
81	fi
82}
83
84function configure_ip() {
85	# Configure the IPs for both interfaces
86	ip netns exec "${NAMESPACE}" ip addr add "${DSTIP}"/24 dev "${DSTIF}"
87	ip netns exec "${NAMESPACE}" ip link set "${DSTIF}" up
88
89	ip addr add "${SRCIP}"/24 dev "${SRCIF}"
90	ip link set "${SRCIF}" up
91}
92
93function select_ipv4_or_ipv6()
94{
95	local VERSION=${1}
96
97	if [[ "$VERSION" == "ipv6" ]]
98	then
99		DSTIP="${DSTIP6}"
100		SRCIP="${SRCIP6}"
101	else
102		DSTIP="${DSTIP4}"
103		SRCIP="${SRCIP4}"
104	fi
105}
106
107function set_network() {
108	local IP_VERSION=${1:-"ipv4"}
109
110	# setup_ns function is coming from lib.sh
111	setup_ns NAMESPACE
112
113	# Create both interfaces, and assign the destination to a different
114	# namespace
115	create_ifaces
116
117	# Link both interfaces back to back
118	link_ifaces
119
120	select_ipv4_or_ipv6 "${IP_VERSION}"
121	configure_ip
122}
123
124function _create_dynamic_target() {
125	local FORMAT="${1:?FORMAT parameter required}"
126	local NCPATH="${2:?NCPATH parameter required}"
127
128	DSTMAC=$(ip netns exec "${NAMESPACE}" \
129		 ip link show "${DSTIF}" | awk '/ether/ {print $2}')
130
131	# Create a dynamic target
132	mkdir "${NCPATH}"
133
134	echo "${DSTIP}" > "${NCPATH}"/remote_ip
135	echo "${SRCIP}" > "${NCPATH}"/local_ip
136	echo "${DSTMAC}" > "${NCPATH}"/remote_mac
137	echo "${SRCIF}" > "${NCPATH}"/dev_name
138
139	if [ "${FORMAT}" == "basic" ]
140	then
141		# Basic target does not support release
142		echo 0 > "${NCPATH}"/release
143		echo 0 > "${NCPATH}"/extended
144	elif [ "${FORMAT}" == "extended" ]
145	then
146		echo 1 > "${NCPATH}"/extended
147	fi
148}
149
150function create_dynamic_target() {
151	local FORMAT=${1:-"extended"}
152	local NCPATH=${2:-"$NETCONS_PATH"}
153	_create_dynamic_target "${FORMAT}" "${NCPATH}"
154
155	echo 1 > "${NCPATH}"/enabled
156
157	# This will make sure that the kernel was able to
158	# load the netconsole driver configuration. The console message
159	# gets more organized/sequential as well.
160	sleep 1
161}
162
163# Generate the command line argument for netconsole following:
164#  netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
165function create_cmdline_str() {
166	local BINDMODE=${1:-"ifname"}
167	if [ "${BINDMODE}" == "ifname" ]
168	then
169		SRCDEV=${SRCIF}
170	else
171		SRCDEV=$(mac_get "${SRCIF}")
172	fi
173
174	DSTMAC=$(ip netns exec "${NAMESPACE}" \
175		 ip link show "${DSTIF}" | awk '/ether/ {print $2}')
176	SRCPORT="1514"
177	TGTPORT="6666"
178
179	echo "netconsole=\"+${SRCPORT}@${SRCIP}/${SRCDEV},${TGTPORT}@${DSTIP}/${DSTMAC}\""
180}
181
182# Do not append the release to the header of the message
183function disable_release_append() {
184	echo 0 > "${NETCONS_PATH}"/enabled
185	echo 0 > "${NETCONS_PATH}"/release
186	echo 1 > "${NETCONS_PATH}"/enabled
187}
188
189function do_cleanup() {
190	local NSIM_DEV_SYS_DEL="/sys/bus/netdevsim/del_device"
191
192	# Delete netdevsim devices
193	echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_DEL"
194	echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_DEL"
195
196	# this is coming from lib.sh
197	cleanup_all_ns
198
199	# Restoring printk configurations
200	echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk
201}
202
203function cleanup_netcons() {
204	# delete netconsole dynamic reconfiguration
205	# do not fail if the target is already disabled
206	local TARGET_PATH=${1:-${NETCONS_PATH}}
207
208	if [[ ! -d "${TARGET_PATH}" ]]
209	then
210		# in some cases this is called before netcons path is created
211		return
212	fi
213	if [[ $(cat "${TARGET_PATH}"/enabled) != 0 ]]
214	then
215		echo 0 > "${TARGET_PATH}"/enabled || true
216	fi
217	# Remove all the keys that got created during the selftest
218	find "${TARGET_PATH}/userdata/" -mindepth 1 -type d -delete
219	# Remove the configfs entry
220	rmdir "${TARGET_PATH}"
221}
222
223function cleanup() {
224	cleanup_netcons
225	do_cleanup
226}
227
228function set_user_data() {
229	if [[ ! -d "${NETCONS_PATH}""/userdata" ]]
230	then
231		echo "Userdata path not available in ${NETCONS_PATH}/userdata"
232		exit "${ksft_skip}"
233	fi
234
235	KEY_PATH="${NETCONS_PATH}/userdata/${USERDATA_KEY}"
236	mkdir -p "${KEY_PATH}"
237	VALUE_PATH="${KEY_PATH}""/value"
238	echo "${USERDATA_VALUE}" > "${VALUE_PATH}"
239}
240
241function listen_port_and_save_to() {
242	local OUTPUT=${1}
243	local IPVERSION=${2:-"ipv4"}
244
245	if [ "${IPVERSION}" == "ipv4" ]
246	then
247		SOCAT_MODE="UDP-LISTEN"
248	else
249		SOCAT_MODE="UDP6-LISTEN"
250	fi
251
252	# Just wait for 3 seconds
253	timeout 3 ip netns exec "${NAMESPACE}" \
254		socat "${SOCAT_MODE}":"${PORT}",fork "${OUTPUT}" 2> /dev/null
255}
256
257# Only validate that the message arrived properly
258function validate_msg() {
259	local TMPFILENAME="$1"
260
261	# Check if the file exists
262	if [ ! -f "$TMPFILENAME" ]; then
263		echo "FAIL: File was not generated." >&2
264		exit "${ksft_fail}"
265	fi
266
267	if ! grep -q "${MSG}" "${TMPFILENAME}"; then
268		echo "FAIL: ${MSG} not found in ${TMPFILENAME}" >&2
269		cat "${TMPFILENAME}" >&2
270		exit "${ksft_fail}"
271	fi
272}
273
274# Validate the message and userdata
275function validate_result() {
276	local TMPFILENAME="$1"
277
278	# TMPFILENAME will contain something like:
279	# 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM
280	#  key=value
281
282	validate_msg "${TMPFILENAME}"
283
284	# userdata is not supported on basic format target,
285	# thus, do not validate it.
286	if [ "${FORMAT}" != "basic" ];
287	then
288		if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then
289			echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2
290			cat "${TMPFILENAME}" >&2
291			exit "${ksft_fail}"
292		fi
293	fi
294
295	# Delete the file once it is validated, otherwise keep it
296	# for debugging purposes
297	rm "${TMPFILENAME}"
298}
299
300function check_for_dependencies() {
301	if [ "$(id -u)" -ne 0 ]; then
302		echo "This test must be run as root" >&2
303		exit "${ksft_skip}"
304	fi
305
306	if ! which socat > /dev/null ; then
307		echo "SKIP: socat(1) is not available" >&2
308		exit "${ksft_skip}"
309	fi
310
311	if ! which ip > /dev/null ; then
312		echo "SKIP: ip(1) is not available" >&2
313		exit "${ksft_skip}"
314	fi
315
316	if ! which udevadm > /dev/null ; then
317		echo "SKIP: udevadm(1) is not available" >&2
318		exit "${ksft_skip}"
319	fi
320
321	if [ ! -f /proc/net/if_inet6 ]; then
322		echo "SKIP: IPv6 not configured. Check if CONFIG_IPV6 is enabled" >&2
323		exit "${ksft_skip}"
324	fi
325
326	if [ ! -f "${NSIM_DEV_SYS_NEW}" ]; then
327		echo "SKIP: file ${NSIM_DEV_SYS_NEW} does not exist. Check if CONFIG_NETDEVSIM is enabled" >&2
328		exit "${ksft_skip}"
329	fi
330
331	if [ ! -d "${NETCONS_CONFIGFS}" ]; then
332		echo "SKIP: directory ${NETCONS_CONFIGFS} does not exist. Check if NETCONSOLE_DYNAMIC is enabled" >&2
333		exit "${ksft_skip}"
334	fi
335
336	if ip link show "${DSTIF}" 2> /dev/null; then
337		echo "SKIP: interface ${DSTIF} exists in the system. Not overwriting it." >&2
338		exit "${ksft_skip}"
339	fi
340
341	REGEXP4="inet.*(${SRCIP4}|${DSTIP4})"
342	REGEXP6="inet.*(${SRCIP6}|${DSTIP6})"
343	if ip addr list | grep -E "${REGEXP4}" 2> /dev/null; then
344		echo "SKIP: IPv4s already in use. Skipping it" >&2
345		exit "${ksft_skip}"
346	fi
347
348	if ip addr list | grep -E "${REGEXP6}" 2> /dev/null; then
349		echo "SKIP: IPv6s already in use. Skipping it" >&2
350		exit "${ksft_skip}"
351	fi
352}
353
354function check_for_taskset() {
355	if ! which taskset > /dev/null ; then
356		echo "SKIP: taskset(1) is not available" >&2
357		exit "${ksft_skip}"
358	fi
359}
360
361# This is necessary if running multiple tests in a row
362function pkill_socat() {
363	PROCESS_NAME4="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}"
364	PROCESS_NAME6="socat UDP6-LISTEN:6666,fork ${OUTPUT_FILE}"
365	# socat runs under timeout(1), kill it if it is still alive
366	# do not fail if socat doesn't exist anymore
367	set +e
368	pkill -f "${PROCESS_NAME4}"
369	pkill -f "${PROCESS_NAME6}"
370	set -e
371}
372
373# Check if netconsole was compiled as a module, otherwise exit
374function check_netconsole_module() {
375	if modinfo netconsole | grep filename: | grep -q builtin
376	then
377		echo "SKIP: netconsole should be compiled as a module" >&2
378		exit "${ksft_skip}"
379	fi
380}
381
382function wait_target_state() {
383	local TARGET=${1}
384	local STATE=${2}
385	local TARGET_PATH="${NETCONS_CONFIGFS}"/"${TARGET}"
386	local ENABLED=0
387
388	if [ "${STATE}" == "enabled" ]
389	then
390		ENABLED=1
391	fi
392
393	if [ ! -d "$TARGET_PATH" ]; then
394		echo "FAIL: Target does not exist." >&2
395		exit "${ksft_fail}"
396	fi
397
398	local CHECK_CMD="grep \"$ENABLED\" \"$TARGET_PATH/enabled\""
399	slowwait 2 sh -c "test -n \"\$($CHECK_CMD)\"" || {
400		echo "FAIL: ${TARGET} is not ${STATE}." >&2
401		exit "${ksft_fail}"
402	}
403}
404
405# A wrapper to translate protocol version to udp version
406function wait_for_port() {
407	local NAMESPACE=${1}
408	local PORT=${2}
409	IP_VERSION=${3}
410
411	if [ "${IP_VERSION}" == "ipv6" ]
412	then
413		PROTOCOL="udp6"
414	else
415		PROTOCOL="udp"
416	fi
417
418	wait_local_port_listen "${NAMESPACE}" "${PORT}" "${PROTOCOL}"
419	# even after the port is open, let's wait 1 second before writing
420	# otherwise the packet could be missed, and the test will fail. Happens
421	# more frequently on IPv6
422	sleep 1
423}
424
425# Clean up netdevsim ifaces created for bonding test
426function cleanup_bond_nsim() {
427	ip -n "${TXNS}" \
428		link delete "${BOND_TX_MAIN_IF}" type bond || true
429	ip -n "${RXNS}" \
430		link delete "${BOND_RX_MAIN_IF}" type bond || true
431
432	cleanup_netdevsim "$NSIM_BOND_TX_1"
433	cleanup_netdevsim "$NSIM_BOND_TX_2"
434	cleanup_netdevsim "$NSIM_BOND_RX_1"
435	cleanup_netdevsim "$NSIM_BOND_RX_2"
436}
437
438# cleanup tests that use bonding interfaces
439function cleanup_bond() {
440	cleanup_netcons
441	cleanup_bond_nsim
442	cleanup_all_ns
443	ip link delete "${VETH0}" || true
444}
445