Skip to content

Commit 236682d

Browse files
leitaokuba-moo
authored andcommitted
selftest: netcons: add test for netconsole over bonded interfaces
This patch adds a selftest that verifies netconsole functionality over bonded network interfaces using netdevsim. It sets up two bonded interfaces acting as transmit (TX) and receive (RX) ends, placed in separate network namespaces. The test sends kernel log messages and verifies that they are properly received on the bonded RX interfaces with both IPv4 and IPv6, and using basic and extended netconsole formats. This patchset aims to test a long-standing netpoll subsystem where netpoll has multiple users. (in this case netconsole and bonding). A similar selftest has been discussed in [1] and [2]. This test also tries to enable bonding and netpoll in different order, just to guarantee that all the possibilities are exercised. Link: https://lore.kernel.org/all/20250905-netconsole_torture-v3-0-875c7febd316@debian.org/ [1] Link: https://lore.kernel.org/lkml/96b940137a50e5c387687bb4f57de8b0435a653f.1404857349.git.decot@googlers.com/ [2] Signed-off-by: Breno Leitao <leitao@debian.org> Reviewed-by: Simon Horman <horms@kernel.org> Link: https://patch.msgid.link/20251107-netconsole_torture-v10-4-749227b55f63@debian.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 6701896 commit 236682d

4 files changed

Lines changed: 414 additions & 7 deletions

File tree

tools/testing/selftests/drivers/net/bonding/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ TEST_PROGS := \
1414
dev_addr_lists.sh \
1515
mode-1-recovery-updelay.sh \
1616
mode-2-recovery-updelay.sh \
17+
netcons_over_bonding.sh \
1718
# end of TEST_PROGS
1819

1920
TEST_FILES := \
@@ -24,6 +25,7 @@ TEST_FILES := \
2425

2526
TEST_INCLUDES := \
2627
../../../net/lib.sh \
28+
../lib/sh/lib_netcons.sh \
2729
../../../net/forwarding/lib.sh \
2830
# end of TEST_INCLUDES
2931

tools/testing/selftests/drivers/net/bonding/config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
CONFIG_BONDING=y
22
CONFIG_BRIDGE=y
3+
CONFIG_CONFIGFS_FS=y
34
CONFIG_DUMMY=y
45
CONFIG_INET_ESP=y
56
CONFIG_INET_ESP_OFFLOAD=y
@@ -9,6 +10,9 @@ CONFIG_MACVLAN=y
910
CONFIG_NET_ACT_GACT=y
1011
CONFIG_NET_CLS_FLOWER=y
1112
CONFIG_NET_CLS_MATCHALL=m
13+
CONFIG_NETCONSOLE=m
14+
CONFIG_NETCONSOLE_DYNAMIC=y
15+
CONFIG_NETCONSOLE_EXTENDED_LOG=y
1216
CONFIG_NETDEVSIM=m
1317
CONFIG_NET_SCH_INGRESS=y
1418
CONFIG_NLMON=y
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
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+
28+
set -euo pipefail
29+
30+
SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")
31+
32+
source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh
33+
34+
modprobe netdevsim 2> /dev/null || true
35+
modprobe netconsole 2> /dev/null || true
36+
modprobe bonding 2> /dev/null || true
37+
modprobe veth 2> /dev/null || true
38+
39+
# The content of kmsg will be save to the following file
40+
OUTPUT_FILE="/tmp/${TARGET}"
41+
42+
# Check for basic system dependency and exit if not found
43+
check_for_dependencies
44+
# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
45+
echo "6 5" > /proc/sys/kernel/printk
46+
# Remove the namespace, interfaces and netconsole target on exit
47+
trap cleanup_bond EXIT
48+
49+
FORMAT="extended"
50+
IP_VERSION="ipv4"
51+
VETH0="veth"$(( RANDOM % 256))
52+
VETH1="veth"$((256 + RANDOM % 256))
53+
TXNS=""
54+
RXNS=""
55+
56+
# Create "bond_tx_XX" and "bond_rx_XX" interfaces, and set DSTIF and SRCIF with
57+
# the bonding interfaces
58+
function 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)
107+
function 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
115+
function 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+
145+
function 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
157+
function 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+
168+
function 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+
188+
function 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
207+
function 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
226+
function 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.
258+
function 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+
274+
function 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
298+
function 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
308+
create_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
313+
setup_bonding_ifaces
314+
315+
# Configure the ips as BOND_RX1_SLAVE_IF and BOND_TX1_SLAVE_IF
316+
configure_ifaces_ips "${IP_VERSION}"
317+
318+
_create_dynamic_target "${FORMAT}" "${NETCONS_PATH}"
319+
enable_netcons_ns
320+
set_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.
325+
test_send_netcons_msg_through_bond_iface
326+
echo "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
331+
test_enable_netpoll_on_enslaved_iface
332+
echo "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
336+
test_delete_bond_and_reenable_target
337+
echo "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
342+
test_enslave_netcons_enabled_iface
343+
echo "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
349+
test_enslave_iface_to_bond
350+
echo "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
356+
test_enslave_iff_disabled_netpoll_iface
357+
echo "test #6: Enslaving IFF_DISABLE_NETPOLL ifaces to bond iface is not supported. Test passed." >&2
358+
359+
cleanup_bond
360+
trap - EXIT
361+
exit "${EXIT_STATUS}"

0 commit comments

Comments
 (0)