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