xref: /linux/tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh (revision d0309c054362a235077327b46f727bc48878a3bc)
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	if [[ ! -d "${NETCONS_PATH}" ]]
207	then
208		# in some cases this is called before netcons path is created
209		return
210	fi
211	if [[ $(cat "${NETCONS_PATH}"/enabled) != 0 ]]
212	then
213		echo 0 > "${NETCONS_PATH}"/enabled || true
214	fi
215	# Remove all the keys that got created during the selftest
216	find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete
217	# Remove the configfs entry
218	rmdir "${NETCONS_PATH}"
219}
220
221function cleanup() {
222	cleanup_netcons
223	do_cleanup
224}
225
226function set_user_data() {
227	if [[ ! -d "${NETCONS_PATH}""/userdata" ]]
228	then
229		echo "Userdata path not available in ${NETCONS_PATH}/userdata"
230		exit "${ksft_skip}"
231	fi
232
233	KEY_PATH="${NETCONS_PATH}/userdata/${USERDATA_KEY}"
234	mkdir -p "${KEY_PATH}"
235	VALUE_PATH="${KEY_PATH}""/value"
236	echo "${USERDATA_VALUE}" > "${VALUE_PATH}"
237}
238
239function listen_port_and_save_to() {
240	local OUTPUT=${1}
241	local IPVERSION=${2:-"ipv4"}
242
243	if [ "${IPVERSION}" == "ipv4" ]
244	then
245		SOCAT_MODE="UDP-LISTEN"
246	else
247		SOCAT_MODE="UDP6-LISTEN"
248	fi
249
250	# Just wait for 2 seconds
251	timeout 2 ip netns exec "${NAMESPACE}" \
252		socat "${SOCAT_MODE}":"${PORT}",fork "${OUTPUT}"
253}
254
255# Only validate that the message arrived properly
256function validate_msg() {
257	local TMPFILENAME="$1"
258
259	# Check if the file exists
260	if [ ! -f "$TMPFILENAME" ]; then
261		echo "FAIL: File was not generated." >&2
262		exit "${ksft_fail}"
263	fi
264
265	if ! grep -q "${MSG}" "${TMPFILENAME}"; then
266		echo "FAIL: ${MSG} not found in ${TMPFILENAME}" >&2
267		cat "${TMPFILENAME}" >&2
268		exit "${ksft_fail}"
269	fi
270}
271
272# Validate the message and userdata
273function validate_result() {
274	local TMPFILENAME="$1"
275
276	# TMPFILENAME will contain something like:
277	# 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM
278	#  key=value
279
280	validate_msg "${TMPFILENAME}"
281
282	# userdata is not supported on basic format target,
283	# thus, do not validate it.
284	if [ "${FORMAT}" != "basic" ];
285	then
286		if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then
287			echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2
288			cat "${TMPFILENAME}" >&2
289			exit "${ksft_fail}"
290		fi
291	fi
292
293	# Delete the file once it is validated, otherwise keep it
294	# for debugging purposes
295	rm "${TMPFILENAME}"
296}
297
298function check_for_dependencies() {
299	if [ "$(id -u)" -ne 0 ]; then
300		echo "This test must be run as root" >&2
301		exit "${ksft_skip}"
302	fi
303
304	if ! which socat > /dev/null ; then
305		echo "SKIP: socat(1) is not available" >&2
306		exit "${ksft_skip}"
307	fi
308
309	if ! which ip > /dev/null ; then
310		echo "SKIP: ip(1) is not available" >&2
311		exit "${ksft_skip}"
312	fi
313
314	if ! which udevadm > /dev/null ; then
315		echo "SKIP: udevadm(1) is not available" >&2
316		exit "${ksft_skip}"
317	fi
318
319	if [ ! -f /proc/net/if_inet6 ]; then
320		echo "SKIP: IPv6 not configured. Check if CONFIG_IPV6 is enabled" >&2
321		exit "${ksft_skip}"
322	fi
323
324	if [ ! -f "${NSIM_DEV_SYS_NEW}" ]; then
325		echo "SKIP: file ${NSIM_DEV_SYS_NEW} does not exist. Check if CONFIG_NETDEVSIM is enabled" >&2
326		exit "${ksft_skip}"
327	fi
328
329	if [ ! -d "${NETCONS_CONFIGFS}" ]; then
330		echo "SKIP: directory ${NETCONS_CONFIGFS} does not exist. Check if NETCONSOLE_DYNAMIC is enabled" >&2
331		exit "${ksft_skip}"
332	fi
333
334	if ip link show "${DSTIF}" 2> /dev/null; then
335		echo "SKIP: interface ${DSTIF} exists in the system. Not overwriting it." >&2
336		exit "${ksft_skip}"
337	fi
338
339	REGEXP4="inet.*(${SRCIP4}|${DSTIP4})"
340	REGEXP6="inet.*(${SRCIP6}|${DSTIP6})"
341	if ip addr list | grep -E "${REGEXP4}" 2> /dev/null; then
342		echo "SKIP: IPv4s already in use. Skipping it" >&2
343		exit "${ksft_skip}"
344	fi
345
346	if ip addr list | grep -E "${REGEXP6}" 2> /dev/null; then
347		echo "SKIP: IPv6s already in use. Skipping it" >&2
348		exit "${ksft_skip}"
349	fi
350}
351
352function check_for_taskset() {
353	if ! which taskset > /dev/null ; then
354		echo "SKIP: taskset(1) is not available" >&2
355		exit "${ksft_skip}"
356	fi
357}
358
359# This is necessary if running multiple tests in a row
360function pkill_socat() {
361	PROCESS_NAME4="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}"
362	PROCESS_NAME6="socat UDP6-LISTEN:6666,fork ${OUTPUT_FILE}"
363	# socat runs under timeout(1), kill it if it is still alive
364	# do not fail if socat doesn't exist anymore
365	set +e
366	pkill -f "${PROCESS_NAME4}"
367	pkill -f "${PROCESS_NAME6}"
368	set -e
369}
370
371# Check if netconsole was compiled as a module, otherwise exit
372function check_netconsole_module() {
373	if modinfo netconsole | grep filename: | grep -q builtin
374	then
375		echo "SKIP: netconsole should be compiled as a module" >&2
376		exit "${ksft_skip}"
377	fi
378}
379
380# A wrapper to translate protocol version to udp version
381function wait_for_port() {
382	local NAMESPACE=${1}
383	local PORT=${2}
384	IP_VERSION=${3}
385
386	if [ "${IP_VERSION}" == "ipv6" ]
387	then
388		PROTOCOL="udp6"
389	else
390		PROTOCOL="udp"
391	fi
392
393	wait_local_port_listen "${NAMESPACE}" "${PORT}" "${PROTOCOL}"
394	# even after the port is open, let's wait 1 second before writing
395	# otherwise the packet could be missed, and the test will fail. Happens
396	# more frequently on IPv6
397	sleep 1
398}
399
400# Clean up netdevsim ifaces created for bonding test
401function cleanup_bond_nsim() {
402	ip -n "${TXNS}" \
403		link delete "${BOND_TX_MAIN_IF}" type bond || true
404	ip -n "${RXNS}" \
405		link delete "${BOND_RX_MAIN_IF}" type bond || true
406
407	cleanup_netdevsim "$NSIM_BOND_TX_1"
408	cleanup_netdevsim "$NSIM_BOND_TX_2"
409	cleanup_netdevsim "$NSIM_BOND_RX_1"
410	cleanup_netdevsim "$NSIM_BOND_RX_2"
411}
412
413# cleanup tests that use bonding interfaces
414function cleanup_bond() {
415	cleanup_netcons
416	cleanup_bond_nsim
417	cleanup_all_ns
418	ip link delete "${VETH0}" || true
419}
420