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