summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/drivers/net/bonding/netcons_over_bonding.sh
blob: 477cc9379500a3a74a20f8a978be5798cc5ffe17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
#
# This selftest exercises trying to have multiple netpoll users at the same
# time.
#
# This selftest has multiple smalls test inside, and the goal is to
# get interfaces with bonding and netconsole in different orders in order
# to catch any possible issue.
#
# The main test composes of four interfaces being created using netdevsim; two
# of them are bonded to serve as the netconsole's transmit interface. The
# remaining two interfaces are similarly bonded and assigned to a separate
# network namespace, which acts as the receive interface, where socat monitors
# for incoming messages.
#
# A netconsole message is then sent to ensure it is properly received across
# this configuration.
#
# Later, run a few other tests, to make sure that bonding and netconsole
# cannot coexist.
#
# The test's objective is to exercise netpoll usage when managed simultaneously
# by multiple subsystems (netconsole and bonding).
#
# Author: Breno Leitao <leitao@debian.org>

set -euo pipefail

SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")

source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh

modprobe netdevsim 2> /dev/null || true
modprobe netconsole 2> /dev/null || true
modprobe bonding 2> /dev/null || true
modprobe veth 2> /dev/null || true

# The content of kmsg will be save to the following file
OUTPUT_FILE="/tmp/${TARGET}"

# Check for basic system dependency and exit if not found
check_for_dependencies
# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5)
echo "6 5" > /proc/sys/kernel/printk
# Remove the namespace, interfaces and netconsole target on exit
trap cleanup_bond EXIT

FORMAT="extended"
IP_VERSION="ipv4"
VETH0="veth"$(( RANDOM % 256))
VETH1="veth"$((256 +  RANDOM % 256))
TXNS=""
RXNS=""

# Create "bond_tx_XX" and "bond_rx_XX" interfaces, and set DSTIF and SRCIF with
# the bonding interfaces
function setup_bonding_ifaces() {
	local RAND=$(( RANDOM % 100 ))
	BOND_TX_MAIN_IF="bond_tx_$RAND"
	BOND_RX_MAIN_IF="bond_rx_$RAND"

	# Setup TX
	if ! ip -n "${TXNS}" link add "${BOND_TX_MAIN_IF}" type bond mode balance-rr
	then
		echo "Failed to create bond TX interface. Is CONFIG_BONDING set?" >&2
		# only clean nsim ifaces and namespace. Nothing else has been
		# initialized
		cleanup_bond_nsim
		trap - EXIT
		exit "${ksft_skip}"
	fi

	# create_netdevsim() got the interface up, but it needs to be down
	# before being enslaved.
	ip -n "${TXNS}" \
		link set "${BOND_TX1_SLAVE_IF}" down
	ip -n "${TXNS}" \
		link set "${BOND_TX2_SLAVE_IF}" down
	ip -n "${TXNS}" \
		link set "${BOND_TX1_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
	ip -n "${TXNS}" \
		link set "${BOND_TX2_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
	ip -n "${TXNS}" \
		link set "${BOND_TX_MAIN_IF}" up

	# Setup RX
	ip -n "${RXNS}" \
		link add "${BOND_RX_MAIN_IF}" type bond mode balance-rr
	ip -n "${RXNS}" \
		link set "${BOND_RX1_SLAVE_IF}" down
	ip -n "${RXNS}" \
		link set "${BOND_RX2_SLAVE_IF}" down
	ip -n "${RXNS}" \
		link set "${BOND_RX1_SLAVE_IF}" master "${BOND_RX_MAIN_IF}"
	ip -n "${RXNS}" \
		link set "${BOND_RX2_SLAVE_IF}" master "${BOND_RX_MAIN_IF}"
	ip -n "${RXNS}" \
		link set "${BOND_RX_MAIN_IF}" up

	export DSTIF="${BOND_RX_MAIN_IF}"
	export SRCIF="${BOND_TX_MAIN_IF}"
}

# Create 4 netdevsim interfaces. Two of them will be bound to TX bonding iface
# and the other two will be bond to the RX interface (on the other namespace)
function create_ifaces_bond() {
	BOND_TX1_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_TX_1}" "${TXNS}")
	BOND_TX2_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_TX_2}" "${TXNS}")
	BOND_RX1_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_RX_1}" "${RXNS}")
	BOND_RX2_SLAVE_IF=$(create_netdevsim "${NSIM_BOND_RX_2}" "${RXNS}")
}

# netdevsim link BOND_TX to BOND_RX interfaces
function link_ifaces_bond() {
	local BOND_TX1_SLAVE_IFIDX
	local BOND_TX2_SLAVE_IFIDX
	local BOND_RX1_SLAVE_IFIDX
	local BOND_RX2_SLAVE_IFIDX
	local TXNS_FD
	local RXNS_FD

	BOND_TX1_SLAVE_IFIDX=$(ip netns exec "${TXNS}" \
				cat /sys/class/net/"$BOND_TX1_SLAVE_IF"/ifindex)
	BOND_TX2_SLAVE_IFIDX=$(ip netns exec "${TXNS}" \
				cat /sys/class/net/"$BOND_TX2_SLAVE_IF"/ifindex)
	BOND_RX1_SLAVE_IFIDX=$(ip netns exec "${RXNS}" \
				cat /sys/class/net/"$BOND_RX1_SLAVE_IF"/ifindex)
	BOND_RX2_SLAVE_IFIDX=$(ip netns exec "${RXNS}" \
				cat /sys/class/net/"$BOND_RX2_SLAVE_IF"/ifindex)

	exec {TXNS_FD}</var/run/netns/"${TXNS}"
	exec {RXNS_FD}</var/run/netns/"${RXNS}"

	# Linking TX ifaces to the RX ones (on the other namespace)
	echo "${TXNS_FD}:$BOND_TX1_SLAVE_IFIDX $RXNS_FD:$BOND_RX1_SLAVE_IFIDX"  \
		> "$NSIM_DEV_SYS_LINK"
	echo "${TXNS_FD}:$BOND_TX2_SLAVE_IFIDX $RXNS_FD:$BOND_RX2_SLAVE_IFIDX"  \
		> "$NSIM_DEV_SYS_LINK"

	exec {TXNS_FD}<&-
	exec {RXNS_FD}<&-
}

function create_all_ifaces() {
	# setup_ns function is coming from lib.sh
	setup_ns TXNS RXNS
	export NAMESPACE="${RXNS}"

	# Create two interfaces for RX and two for TX
	create_ifaces_bond
	# Link netlink ifaces
	link_ifaces_bond
}

# configure DSTIF and SRCIF IPs
function configure_ifaces_ips() {
	local IP_VERSION=${1:-"ipv4"}
	select_ipv4_or_ipv6 "${IP_VERSION}"

	ip -n "${RXNS}" addr add "${DSTIP}"/24 dev "${DSTIF}"
	ip -n "${RXNS}" link set "${DSTIF}" up

	ip -n "${TXNS}" addr add "${SRCIP}"/24 dev "${SRCIF}"
	ip -n "${TXNS}" link set "${SRCIF}" up
}

function test_enable_netpoll_on_enslaved_iface() {
	echo 0 > "${NETCONS_PATH}"/enabled

	# At this stage, BOND_TX1_SLAVE_IF is enslaved to BOND_TX_MAIN_IF, and
	# linked to BOND_RX1_SLAVE_IF inside the namespace.
	echo "${BOND_TX1_SLAVE_IF}" > "${NETCONS_PATH}"/dev_name

	# This should fail with the following message in dmesg:
	# netpoll: netconsole: ethX is a slave device, aborting
	set +e
	enable_netcons_ns 2> /dev/null
	set -e

	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 1 ]]
	then
		echo "test failed: Bonding and netpoll cannot co-exists." >&2
		exit "${ksft_fail}"
	fi
}

function test_delete_bond_and_reenable_target() {
	ip -n "${TXNS}" \
		link delete "${BOND_TX_MAIN_IF}" type bond

	# BOND_TX1_SLAVE_IF is not attached to a bond interface anymore
	# netpoll can be plugged in there
	echo "${BOND_TX1_SLAVE_IF}" > "${NETCONS_PATH}"/dev_name

	# this should work, since the interface is not enslaved
	enable_netcons_ns

	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
	then
		echo "test failed: Unable to start netpoll on an unbond iface." >&2
		exit "${ksft_fail}"
	fi
}

# Send a netconsole message to the netconsole target
function test_send_netcons_msg_through_bond_iface() {
	# Listen for netconsole port inside the namespace and
	# destination interface
	listen_port_and_save_to "${OUTPUT_FILE}" "${IP_VERSION}" &
	# Wait for socat to start and listen to the port.
	wait_for_port "${RXNS}" "${PORT}" "${IP_VERSION}"
	# Send the message
	echo "${MSG}: ${TARGET}" > /dev/kmsg
	# Wait until socat saves the file to disk
	busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}"
	# Make sure the message was received in the dst part
	# and exit
	validate_result "${OUTPUT_FILE}" "${FORMAT}"
	# kill socat in case it is still running
	pkill_socat
}

# BOND_TX1_SLAVE_IF has netconsole enabled on it, bind it to BOND_TX_MAIN_IF.
# Given BOND_TX_MAIN_IF was deleted, recreate it first
function test_enslave_netcons_enabled_iface {
	# netconsole got disabled while the interface was down
	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
	then
		echo "test failed: netconsole expected to be enabled against BOND_TX1_SLAVE_IF" >&2
		exit "${ksft_fail}"
	fi

	# recreate the bonding iface. it got deleted by previous
	# test (test_delete_bond_and_reenable_target)
	ip -n "${TXNS}" \
		link add "${BOND_TX_MAIN_IF}" type bond mode balance-rr

	# sub-interface need to be down before attaching to bonding
	# This will also disable netconsole.
	ip -n "${TXNS}" \
		link set "${BOND_TX1_SLAVE_IF}" down
	ip -n "${TXNS}" \
		link set "${BOND_TX1_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
	ip -n "${TXNS}" \
		link set "${BOND_TX_MAIN_IF}" up

	# netconsole got disabled while the interface was down
	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 1 ]]
	then
		echo "test failed: Device is part of a bond iface, cannot have netcons enabled" >&2
		exit "${ksft_fail}"
	fi
}

# Get netconsole enabled on a bonding interface and attach a second
# sub-interface.
function test_enslave_iface_to_bond {
	# BOND_TX_MAIN_IF has only BOND_TX1_SLAVE_IF right now
	echo "${BOND_TX_MAIN_IF}" > "${NETCONS_PATH}"/dev_name
	enable_netcons_ns

	# netcons is attached to bond0 and BOND_TX1_SLAVE_IF is
	# part of BOND_TX_MAIN_IF. Attach BOND_TX2_SLAVE_IF to BOND_TX_MAIN_IF.
	ip -n "${TXNS}" \
		link set "${BOND_TX2_SLAVE_IF}" master "${BOND_TX_MAIN_IF}"
	if [[ $(cat "${NETCONS_PATH}"/enabled) -eq 0 ]]
	then
		echo "test failed: Netconsole should be enabled on bonding interface. Failed" >&2
		exit "${ksft_fail}"
	fi
}

function test_enslave_iff_disabled_netpoll_iface {
	local ret

	# Create two interfaces. veth interfaces it known to have
	# IFF_DISABLE_NETPOLL set
	if ! ip link add "${VETH0}" type veth peer name "${VETH1}"
	then
		echo "Failed to create veth TX interface. Is CONFIG_VETH set?" >&2
		exit "${ksft_skip}"
	fi
	set +e
	# This will print RTNETLINK answers: Device or resource busy
	ip link set "${VETH0}" master "${BOND_TX_MAIN_IF}" 2> /dev/null
	ret=$?
	set -e
	if [[ $ret -eq 0 ]]
	then
		echo "test failed: veth interface could not be enslaved"
		exit "${ksft_fail}"
	fi
}

# Given that netconsole picks the current net namespace, we need to enable it
# from inside the TXNS namespace
function enable_netcons_ns() {
	ip netns exec "${TXNS}" sh -c \
		"mount -t configfs configfs /sys/kernel/config && echo 1 > $NETCONS_PATH/enabled"
}

####################
# Tests start here #
####################

# Create regular interfaces using netdevsim and link them
create_all_ifaces

# Setup the bonding interfaces
# BOND_RX_MAIN_IF has BOND_RX{1,2}_SLAVE_IF
# BOND_TX_MAIN_IF has BOND_TX{1,2}_SLAVE_IF
setup_bonding_ifaces

# Configure the ips as BOND_RX1_SLAVE_IF and BOND_TX1_SLAVE_IF
configure_ifaces_ips "${IP_VERSION}"

_create_dynamic_target "${FORMAT}" "${NETCONS_PATH}"
enable_netcons_ns
set_user_data

# Test #1 : Create an bonding interface and attach netpoll into
# the bonding interface. Netconsole/netpoll should work on
# the bonding interface.
test_send_netcons_msg_through_bond_iface
echo "test #1: netpoll on bonding interface worked. Test passed" >&2

# Test #2: Attach netpoll to an enslaved interface
# Try to attach netpoll to an enslaved sub-interface (while still being part of
# a bonding interface), which shouldn't be allowed
test_enable_netpoll_on_enslaved_iface
echo "test #2: netpoll correctly rejected enslaved interface (expected behavior). Test passed." >&2

# Test #3: Unplug the sub-interface from bond and enable netconsole
# Detach the interface from a bonding interface and attach netpoll again
test_delete_bond_and_reenable_target
echo "test #3: Able to attach to an unbound interface. Test passed." >&2

# Test #4: Enslave a sub-interface that had netconsole enabled
# Try to enslave an interface that has netconsole/netpoll enabled.
# Previous test has netconsole enabled in BOND_TX1_SLAVE_IF, try to enslave it
test_enslave_netcons_enabled_iface
echo "test #4: Enslaving an interface with netpoll attached. Test passed." >&2

# Test #5: Enslave a sub-interface to a bonding interface
# Enslave an interface to a bond interface that has netpoll attached
# At this stage, BOND_TX_MAIN_IF is created and BOND_TX1_SLAVE_IF is part of
# it. Netconsole is currently disabled
test_enslave_iface_to_bond
echo "test #5: Enslaving an interface to bond+netpoll. Test passed." >&2

# Test #6: Enslave a IFF_DISABLE_NETPOLL sub-interface to a bonding interface
# At this stage, BOND_TX_MAIN_IF has both sub interface and netconsole is
# enabled. This test will try to enslave an a veth (IFF_DISABLE_NETPOLL) interface
# and it should fail, with netpoll: veth0 doesn't support polling
test_enslave_iff_disabled_netpoll_iface
echo "test #6: Enslaving IFF_DISABLE_NETPOLL ifaces to bond iface is not supported. Test passed." >&2

cleanup_bond
trap - EXIT
exit "${EXIT_STATUS}"