xref: /linux/tools/testing/selftests/drivers/net/bonding/netcons_over_bonding.sh (revision d0309c054362a235077327b46f727bc48878a3bc)
1#!/usr/bin/env bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# This selftest exercises trying to have multiple netpoll users at the same
5# time.
6#
7# This selftest has multiple smalls test inside, and the goal is to
8# get interfaces with bonding and netconsole in different orders in order
9# to catch any possible issue.
10#
11# The main test composes of four interfaces being created using netdevsim; two
12# of them are bonded to serve as the netconsole's transmit interface. The
13# remaining two interfaces are similarly bonded and assigned to a separate
14# network namespace, which acts as the receive interface, where socat monitors
15# for incoming messages.
16#
17# A netconsole message is then sent to ensure it is properly received across
18# this configuration.
19#
20# Later, run a few other tests, to make sure that bonding and netconsole
21# cannot coexist.
22#
23# The test's objective is to exercise netpoll usage when managed simultaneously
24# by multiple subsystems (netconsole and bonding).
25#
26# Author: Breno Leitao <leitao@debian.org>
27
28set -euo pipefail
29
30SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
31
32source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh
33
34modprobe netdevsim 2> /dev/null || true
35modprobe netconsole 2> /dev/null || true
36modprobe bonding 2> /dev/null || true
37modprobe veth 2> /dev/null || true
38
39# The content of kmsg will be save to the following file
40OUTPUT_FILE="/tmp/${TARGET}"
41
42# Check for basic system dependency and exit if not found
43check_for_dependencies
44# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
45echo "6 5" > /proc/sys/kernel/printk
46# Remove the namespace, interfaces and netconsole target on exit
47trap cleanup_bond EXIT
48
49FORMAT="extended"
50IP_VERSION="ipv4"
51VETH0="veth"$(( RANDOM % 256))
52VETH1="veth"$((256 +  RANDOM % 256))
53TXNS=""
54RXNS=""
55
56# Create "bond_tx_XX" and "bond_rx_XX" interfaces, and set DSTIF and SRCIF with
57# the bonding interfaces
58function setup_bonding_ifaces() {
59	local RAND=$(( RANDOM % 100 ))
60	BOND_TX_MAIN_IF="bond_tx_$RAND"
61	BOND_RX_MAIN_IF="bond_rx_$RAND"
62
63	# Setup TX
64	if ! ip -n "${TXNS}" link add "${BOND_TX_MAIN_IF}" type bond mode balance-rr
65	then
66		echo "Failed to create bond TX interface. Is CONFIG_BONDING set?" >&2
67		# only clean nsim ifaces and namespace. Nothing else has been
68		# initialized
69		cleanup_bond_nsim
70		trap - EXIT
71		exit "${ksft_skip}"
72	fi
73
74	# create_netdevsim() got the interface up, but it needs to be down
75	# before being enslaved.
76	ip -n "${TXNS}" \
77		link set "${BOND_TX1_SLAVE_IF}" down
78	ip -n "${TXNS}" \
79		link set "${BOND_TX2_SLAVE_IF}" down
80	ip -n "${TXNS}" \
81		link set "${BOND_TX1_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
82	ip -n "${TXNS}" \
83		link set "${BOND_TX2_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
84	ip -n "${TXNS}" \
85		link set "${BOND_TX_MAIN_IF}" up
86
87	# Setup RX
88	ip -n "${RXNS}" \
89		link add "${BOND_RX_MAIN_IF}" type bond mode balance-rr
90	ip -n "${RXNS}" \
91		link set "${BOND_RX1_SLAVE_IF}" down
92	ip -n "${RXNS}" \
93		link set "${BOND_RX2_SLAVE_IF}" down
94	ip -n "${RXNS}" \
95		link set "${BOND_RX1_SLAVE_IF}" master "${BOND_RX_MAIN_IF}"
96	ip -n "${RXNS}" \
97		link set "${BOND_RX2_SLAVE_IF}" master "${BOND_RX_MAIN_IF}"
98	ip -n "${RXNS}" \
99		link set "${BOND_RX_MAIN_IF}" up
100
101	export DSTIF="${BOND_RX_MAIN_IF}"
102	export SRCIF="${BOND_TX_MAIN_IF}"
103}
104
105# Create 4 netdevsim interfaces. Two of them will be bound to TX bonding iface
106# and the other two will be bond to the RX interface (on the other namespace)
107function create_ifaces_bond() {
108	BOND_TX1_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_TX_1}" "${TXNS}")
109	BOND_TX2_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_TX_2}" "${TXNS}")
110	BOND_RX1_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_RX_1}" "${RXNS}")
111	BOND_RX2_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_RX_2}" "${RXNS}")
112}
113
114# netdevsim link BOND_TX to BOND_RX interfaces
115function link_ifaces_bond() {
116	local BOND_TX1_SLAVE_IFIDX
117	local BOND_TX2_SLAVE_IFIDX
118	local BOND_RX1_SLAVE_IFIDX
119	local BOND_RX2_SLAVE_IFIDX
120	local TXNS_FD
121	local RXNS_FD
122
123	BOND_TX1_SLAVE_IFIDX=$(ip netns exec "${TXNS}" \
124				cat /sys/class/net/"$BOND_TX1_SLAVE_IF"/ifindex)
125	BOND_TX2_SLAVE_IFIDX=$(ip netns exec "${TXNS}" \
126				cat /sys/class/net/"$BOND_TX2_SLAVE_IF"/ifindex)
127	BOND_RX1_SLAVE_IFIDX=$(ip netns exec "${RXNS}" \
128				cat /sys/class/net/"$BOND_RX1_SLAVE_IF"/ifindex)
129	BOND_RX2_SLAVE_IFIDX=$(ip netns exec "${RXNS}" \
130				cat /sys/class/net/"$BOND_RX2_SLAVE_IF"/ifindex)
131
132	exec {TXNS_FD}</var/run/netns/"${TXNS}"
133	exec {RXNS_FD}</var/run/netns/"${RXNS}"
134
135	# Linking TX ifaces to the RX ones (on the other namespace)
136	echo "${TXNS_FD}:$BOND_TX1_SLAVE_IFIDX $RXNS_FD:$BOND_RX1_SLAVE_IFIDX"  \
137		> "$NSIM_DEV_SYS_LINK"
138	echo "${TXNS_FD}:$BOND_TX2_SLAVE_IFIDX $RXNS_FD:$BOND_RX2_SLAVE_IFIDX"  \
139		> "$NSIM_DEV_SYS_LINK"
140
141	exec {TXNS_FD}<&-
142	exec {RXNS_FD}<&-
143}
144
145function create_all_ifaces() {
146	# setup_ns function is coming from lib.sh
147	setup_ns TXNS RXNS
148	export NAMESPACE="${RXNS}"
149
150	# Create two interfaces for RX and two for TX
151	create_ifaces_bond
152	# Link netlink ifaces
153	link_ifaces_bond
154}
155
156# configure DSTIF and SRCIF IPs
157function configure_ifaces_ips() {
158	local IP_VERSION=${1:-"ipv4"}
159	select_ipv4_or_ipv6 "${IP_VERSION}"
160
161	ip -n "${RXNS}" addr add "${DSTIP}"/24 dev "${DSTIF}"
162	ip -n "${RXNS}" link set "${DSTIF}" up
163
164	ip -n "${TXNS}" addr add "${SRCIP}"/24 dev "${SRCIF}"
165	ip -n "${TXNS}" link set "${SRCIF}" up
166}
167
168function test_enable_netpoll_on_enslaved_iface() {
169	echo 0 > "${NETCONS_PATH}"/enabled
170
171	# At this stage, BOND_TX1_SLAVE_IF is enslaved to BOND_TX_MAIN_IF, and
172	# linked to BOND_RX1_SLAVE_IF inside the namespace.
173	echo "${BOND_TX1_SLAVE_IF}" > "${NETCONS_PATH}"/dev_name
174
175	# This should fail with the following message in dmesg:
176	# netpoll: netconsole: ethX is a slave device, aborting
177	set +e
178	enable_netcons_ns 2> /dev/null
179	set -e
180
181	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 1 ]]
182	then
183		echo "test failed: Bonding and netpoll cannot co-exists." >&2
184		exit "${ksft_fail}"
185	fi
186}
187
188function test_delete_bond_and_reenable_target() {
189	ip -n "${TXNS}" \
190		link delete "${BOND_TX_MAIN_IF}" type bond
191
192	# BOND_TX1_SLAVE_IF is not attached to a bond interface anymore
193	# netpoll can be plugged in there
194	echo "${BOND_TX1_SLAVE_IF}" > "${NETCONS_PATH}"/dev_name
195
196	# this should work, since the interface is not enslaved
197	enable_netcons_ns
198
199	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
200	then
201		echo "test failed: Unable to start netpoll on an unbond iface." >&2
202		exit "${ksft_fail}"
203	fi
204}
205
206# Send a netconsole message to the netconsole target
207function test_send_netcons_msg_through_bond_iface() {
208	# Listen for netconsole port inside the namespace and
209	# destination interface
210	listen_port_and_save_to "${OUTPUT_FILE}" "${IP_VERSION}" &
211	# Wait for socat to start and listen to the port.
212	wait_for_port "${RXNS}" "${PORT}" "${IP_VERSION}"
213	# Send the message
214	echo "${MSG}: ${TARGET}" > /dev/kmsg
215	# Wait until socat saves the file to disk
216	busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}"
217	# Make sure the message was received in the dst part
218	# and exit
219	validate_result "${OUTPUT_FILE}" "${FORMAT}"
220	# kill socat in case it is still running
221	pkill_socat
222}
223
224# BOND_TX1_SLAVE_IF has netconsole enabled on it, bind it to BOND_TX_MAIN_IF.
225# Given BOND_TX_MAIN_IF was deleted, recreate it first
226function test_enslave_netcons_enabled_iface {
227	# netconsole got disabled while the interface was down
228	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
229	then
230		echo "test failed: netconsole expected to be enabled against BOND_TX1_SLAVE_IF" >&2
231		exit "${ksft_fail}"
232	fi
233
234	# recreate the bonding iface. it got deleted by previous
235	# test (test_delete_bond_and_reenable_target)
236	ip -n "${TXNS}" \
237		link add "${BOND_TX_MAIN_IF}" type bond mode balance-rr
238
239	# sub-interface need to be down before attaching to bonding
240	# This will also disable netconsole.
241	ip -n "${TXNS}" \
242		link set "${BOND_TX1_SLAVE_IF}" down
243	ip -n "${TXNS}" \
244		link set "${BOND_TX1_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
245	ip -n "${TXNS}" \
246		link set "${BOND_TX_MAIN_IF}" up
247
248	# netconsole got disabled while the interface was down
249	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 1 ]]
250	then
251		echo "test failed: Device is part of a bond iface, cannot have netcons enabled" >&2
252		exit "${ksft_fail}"
253	fi
254}
255
256# Get netconsole enabled on a bonding interface and attach a second
257# sub-interface.
258function test_enslave_iface_to_bond {
259	# BOND_TX_MAIN_IF has only BOND_TX1_SLAVE_IF right now
260	echo "${BOND_TX_MAIN_IF}" > "${NETCONS_PATH}"/dev_name
261	enable_netcons_ns
262
263	# netcons is attached to bond0 and BOND_TX1_SLAVE_IF is
264	# part of BOND_TX_MAIN_IF. Attach BOND_TX2_SLAVE_IF to BOND_TX_MAIN_IF.
265	ip -n "${TXNS}" \
266		link set "${BOND_TX2_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
267	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
268	then
269		echo "test failed: Netconsole should be enabled on bonding interface. Failed" >&2
270		exit "${ksft_fail}"
271	fi
272}
273
274function test_enslave_iff_disabled_netpoll_iface {
275	local ret
276
277	# Create two interfaces. veth interfaces it known to have
278	# IFF_DISABLE_NETPOLL set
279	if ! ip link add "${VETH0}" type veth peer name "${VETH1}"
280	then
281		echo "Failed to create veth TX interface. Is CONFIG_VETH set?" >&2
282		exit "${ksft_skip}"
283	fi
284	set +e
285	# This will print RTNETLINK answers: Device or resource busy
286	ip link set "${VETH0}" master "${BOND_TX_MAIN_IF}" 2> /dev/null
287	ret=$?
288	set -e
289	if [[ $ret -eq 0 ]]
290	then
291		echo "test failed: veth interface could not be enslaved"
292		exit "${ksft_fail}"
293	fi
294}
295
296# Given that netconsole picks the current net namespace, we need to enable it
297# from inside the TXNS namespace
298function enable_netcons_ns() {
299	ip netns exec "${TXNS}" sh -c \
300		"mount -t configfs configfs /sys/kernel/config && echo 1 > $NETCONS_PATH/enabled"
301}
302
303####################
304# Tests start here #
305####################
306
307# Create regular interfaces using netdevsim and link them
308create_all_ifaces
309
310# Setup the bonding interfaces
311# BOND_RX_MAIN_IF has BOND_RX{1,2}_SLAVE_IF
312# BOND_TX_MAIN_IF has BOND_TX{1,2}_SLAVE_IF
313setup_bonding_ifaces
314
315# Configure the ips as BOND_RX1_SLAVE_IF and BOND_TX1_SLAVE_IF
316configure_ifaces_ips "${IP_VERSION}"
317
318_create_dynamic_target "${FORMAT}" "${NETCONS_PATH}"
319enable_netcons_ns
320set_user_data
321
322# Test #1 : Create an bonding interface and attach netpoll into
323# the bonding interface. Netconsole/netpoll should work on
324# the bonding interface.
325test_send_netcons_msg_through_bond_iface
326echo "test #1: netpoll on bonding interface worked. Test passed" >&2
327
328# Test #2: Attach netpoll to an enslaved interface
329# Try to attach netpoll to an enslaved sub-interface (while still being part of
330# a bonding interface), which shouldn't be allowed
331test_enable_netpoll_on_enslaved_iface
332echo "test #2: netpoll correctly rejected enslaved interface (expected behavior). Test passed." >&2
333
334# Test #3: Unplug the sub-interface from bond and enable netconsole
335# Detach the interface from a bonding interface and attach netpoll again
336test_delete_bond_and_reenable_target
337echo "test #3: Able to attach to an unbound interface. Test passed." >&2
338
339# Test #4: Enslave a sub-interface that had netconsole enabled
340# Try to enslave an interface that has netconsole/netpoll enabled.
341# Previous test has netconsole enabled in BOND_TX1_SLAVE_IF, try to enslave it
342test_enslave_netcons_enabled_iface
343echo "test #4: Enslaving an interface with netpoll attached. Test passed." >&2
344
345# Test #5: Enslave a sub-interface to a bonding interface
346# Enslave an interface to a bond interface that has netpoll attached
347# At this stage, BOND_TX_MAIN_IF is created and BOND_TX1_SLAVE_IF is part of
348# it. Netconsole is currently disabled
349test_enslave_iface_to_bond
350echo "test #5: Enslaving an interface to bond+netpoll. Test passed." >&2
351
352# Test #6: Enslave a IFF_DISABLE_NETPOLL sub-interface to a bonding interface
353# At this stage, BOND_TX_MAIN_IF has both sub interface and netconsole is
354# enabled. This test will try to enslave an a veth (IFF_DISABLE_NETPOLL) interface
355# and it should fail, with netpoll: veth0 doesn't support polling
356test_enslave_iff_disabled_netpoll_iface
357echo "test #6: Enslaving IFF_DISABLE_NETPOLL ifaces to bond iface is not supported. Test passed." >&2
358
359cleanup_bond
360trap - EXIT
361exit "${EXIT_STATUS}"
362