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