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 14SRCIP4="192.0.2.1" 15SRCIP6="fc00::1" 16DSTIF="" # to be populated later 17DSTIP4="192.0.2.2" 18DSTIP6="fc00::2" 19 20PORT="6666" 21MSG="netconsole selftest" 22USERDATA_KEY="key" 23USERDATA_VALUE="value" 24TARGET=$(mktemp -u netcons_XXXXX) 25DEFAULT_PRINTK_VALUES=$(cat /proc/sys/kernel/printk) 26NETCONS_CONFIGFS="/sys/kernel/config/netconsole" 27NETCONS_PATH="${NETCONS_CONFIGFS}"/"${TARGET}" 28# NAMESPACE will be populated by setup_ns with a random value 29NAMESPACE="" 30 31# IDs for netdevsim 32NSIM_DEV_1_ID=$((256 + RANDOM % 256)) 33NSIM_DEV_2_ID=$((512 + RANDOM % 256)) 34NSIM_DEV_SYS_NEW="/sys/bus/netdevsim/new_device" 35 36# Used to create and delete namespaces 37source "${LIBDIR}"/../../../../net/lib.sh 38 39# Create netdevsim interfaces 40create_ifaces() { 41 42 echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_NEW" 43 echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_NEW" 44 udevadm settle 2> /dev/null || true 45 46 local NSIM1=/sys/bus/netdevsim/devices/netdevsim"$NSIM_DEV_1_ID" 47 local NSIM2=/sys/bus/netdevsim/devices/netdevsim"$NSIM_DEV_2_ID" 48 49 # These are global variables 50 SRCIF=$(find "$NSIM1"/net -maxdepth 1 -type d ! \ 51 -path "$NSIM1"/net -exec basename {} \;) 52 DSTIF=$(find "$NSIM2"/net -maxdepth 1 -type d ! \ 53 -path "$NSIM2"/net -exec basename {} \;) 54} 55 56link_ifaces() { 57 local NSIM_DEV_SYS_LINK="/sys/bus/netdevsim/link_device" 58 local SRCIF_IFIDX=$(cat /sys/class/net/"$SRCIF"/ifindex) 59 local DSTIF_IFIDX=$(cat /sys/class/net/"$DSTIF"/ifindex) 60 61 exec {NAMESPACE_FD}</var/run/netns/"${NAMESPACE}" 62 exec {INITNS_FD}</proc/self/ns/net 63 64 # Bind the dst interface to namespace 65 ip link set "${DSTIF}" netns "${NAMESPACE}" 66 67 # Linking one device to the other one (on the other namespace} 68 if ! echo "${INITNS_FD}:$SRCIF_IFIDX $NAMESPACE_FD:$DSTIF_IFIDX" > $NSIM_DEV_SYS_LINK 69 then 70 echo "linking netdevsim1 with netdevsim2 should succeed" 71 cleanup 72 exit "${ksft_skip}" 73 fi 74} 75 76function configure_ip() { 77 # Configure the IPs for both interfaces 78 ip netns exec "${NAMESPACE}" ip addr add "${DSTIP}"/24 dev "${DSTIF}" 79 ip netns exec "${NAMESPACE}" ip link set "${DSTIF}" up 80 81 ip addr add "${SRCIP}"/24 dev "${SRCIF}" 82 ip link set "${SRCIF}" up 83} 84 85function select_ipv4_or_ipv6() 86{ 87 local VERSION=${1} 88 89 if [[ "$VERSION" == "ipv6" ]] 90 then 91 DSTIP="${DSTIP6}" 92 SRCIP="${SRCIP6}" 93 else 94 DSTIP="${DSTIP4}" 95 SRCIP="${SRCIP4}" 96 fi 97} 98 99function set_network() { 100 local IP_VERSION=${1:-"ipv4"} 101 102 # setup_ns function is coming from lib.sh 103 setup_ns NAMESPACE 104 105 # Create both interfaces, and assign the destination to a different 106 # namespace 107 create_ifaces 108 109 # Link both interfaces back to back 110 link_ifaces 111 112 select_ipv4_or_ipv6 "${IP_VERSION}" 113 configure_ip 114} 115 116function _create_dynamic_target() { 117 local FORMAT="${1:?FORMAT parameter required}" 118 local NCPATH="${2:?NCPATH parameter required}" 119 120 DSTMAC=$(ip netns exec "${NAMESPACE}" \ 121 ip link show "${DSTIF}" | awk '/ether/ {print $2}') 122 123 # Create a dynamic target 124 mkdir "${NCPATH}" 125 126 echo "${DSTIP}" > "${NCPATH}"/remote_ip 127 echo "${SRCIP}" > "${NCPATH}"/local_ip 128 echo "${DSTMAC}" > "${NCPATH}"/remote_mac 129 echo "${SRCIF}" > "${NCPATH}"/dev_name 130 131 if [ "${FORMAT}" == "basic" ] 132 then 133 # Basic target does not support release 134 echo 0 > "${NCPATH}"/release 135 echo 0 > "${NCPATH}"/extended 136 elif [ "${FORMAT}" == "extended" ] 137 then 138 echo 1 > "${NCPATH}"/extended 139 fi 140 141 echo 1 > "${NCPATH}"/enabled 142 143} 144 145function create_dynamic_target() { 146 local FORMAT=${1:-"extended"} 147 local NCPATH=${2:-"$NETCONS_PATH"} 148 _create_dynamic_target "${FORMAT}" "${NCPATH}" 149 150 # This will make sure that the kernel was able to 151 # load the netconsole driver configuration. The console message 152 # gets more organized/sequential as well. 153 sleep 1 154} 155 156# Generate the command line argument for netconsole following: 157# netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr] 158function create_cmdline_str() { 159 local BINDMODE=${1:-"ifname"} 160 if [ "${BINDMODE}" == "ifname" ] 161 then 162 SRCDEV=${SRCIF} 163 else 164 SRCDEV=$(mac_get "${SRCIF}") 165 fi 166 167 DSTMAC=$(ip netns exec "${NAMESPACE}" \ 168 ip link show "${DSTIF}" | awk '/ether/ {print $2}') 169 SRCPORT="1514" 170 TGTPORT="6666" 171 172 echo "netconsole=\"+${SRCPORT}@${SRCIP}/${SRCDEV},${TGTPORT}@${DSTIP}/${DSTMAC}\"" 173} 174 175# Do not append the release to the header of the message 176function disable_release_append() { 177 echo 0 > "${NETCONS_PATH}"/enabled 178 echo 0 > "${NETCONS_PATH}"/release 179 echo 1 > "${NETCONS_PATH}"/enabled 180} 181 182function do_cleanup() { 183 local NSIM_DEV_SYS_DEL="/sys/bus/netdevsim/del_device" 184 185 # Delete netdevsim devices 186 echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_DEL" 187 echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_DEL" 188 189 # this is coming from lib.sh 190 cleanup_all_ns 191 192 # Restoring printk configurations 193 echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk 194} 195 196function cleanup() { 197 # delete netconsole dynamic reconfiguration 198 echo 0 > "${NETCONS_PATH}"/enabled 199 # Remove all the keys that got created during the selftest 200 find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete 201 # Remove the configfs entry 202 rmdir "${NETCONS_PATH}" 203 204 do_cleanup 205} 206 207function set_user_data() { 208 if [[ ! -d "${NETCONS_PATH}""/userdata" ]] 209 then 210 echo "Userdata path not available in ${NETCONS_PATH}/userdata" 211 exit "${ksft_skip}" 212 fi 213 214 KEY_PATH="${NETCONS_PATH}/userdata/${USERDATA_KEY}" 215 mkdir -p "${KEY_PATH}" 216 VALUE_PATH="${KEY_PATH}""/value" 217 echo "${USERDATA_VALUE}" > "${VALUE_PATH}" 218} 219 220function listen_port_and_save_to() { 221 local OUTPUT=${1} 222 local IPVERSION=${2:-"ipv4"} 223 224 if [ "${IPVERSION}" == "ipv4" ] 225 then 226 SOCAT_MODE="UDP-LISTEN" 227 else 228 SOCAT_MODE="UDP6-LISTEN" 229 fi 230 231 # Just wait for 2 seconds 232 timeout 2 ip netns exec "${NAMESPACE}" \ 233 socat "${SOCAT_MODE}":"${PORT}",fork "${OUTPUT}" 234} 235 236# Only validate that the message arrived properly 237function validate_msg() { 238 local TMPFILENAME="$1" 239 240 # Check if the file exists 241 if [ ! -f "$TMPFILENAME" ]; then 242 echo "FAIL: File was not generated." >&2 243 exit "${ksft_fail}" 244 fi 245 246 if ! grep -q "${MSG}" "${TMPFILENAME}"; then 247 echo "FAIL: ${MSG} not found in ${TMPFILENAME}" >&2 248 cat "${TMPFILENAME}" >&2 249 exit "${ksft_fail}" 250 fi 251} 252 253# Validate the message and userdata 254function validate_result() { 255 local TMPFILENAME="$1" 256 257 # TMPFILENAME will contain something like: 258 # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM 259 # key=value 260 261 validate_msg "${TMPFILENAME}" 262 263 # userdata is not supported on basic format target, 264 # thus, do not validate it. 265 if [ "${FORMAT}" != "basic" ]; 266 then 267 if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then 268 echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2 269 cat "${TMPFILENAME}" >&2 270 exit "${ksft_fail}" 271 fi 272 fi 273 274 # Delete the file once it is validated, otherwise keep it 275 # for debugging purposes 276 rm "${TMPFILENAME}" 277} 278 279function check_for_dependencies() { 280 if [ "$(id -u)" -ne 0 ]; then 281 echo "This test must be run as root" >&2 282 exit "${ksft_skip}" 283 fi 284 285 if ! which socat > /dev/null ; then 286 echo "SKIP: socat(1) is not available" >&2 287 exit "${ksft_skip}" 288 fi 289 290 if ! which ip > /dev/null ; then 291 echo "SKIP: ip(1) is not available" >&2 292 exit "${ksft_skip}" 293 fi 294 295 if ! which udevadm > /dev/null ; then 296 echo "SKIP: udevadm(1) is not available" >&2 297 exit "${ksft_skip}" 298 fi 299 300 if [ ! -f /proc/net/if_inet6 ]; then 301 echo "SKIP: IPv6 not configured. Check if CONFIG_IPV6 is enabled" >&2 302 exit "${ksft_skip}" 303 fi 304 305 if [ ! -f "${NSIM_DEV_SYS_NEW}" ]; then 306 echo "SKIP: file ${NSIM_DEV_SYS_NEW} does not exist. Check if CONFIG_NETDEVSIM is enabled" >&2 307 exit "${ksft_skip}" 308 fi 309 310 if [ ! -d "${NETCONS_CONFIGFS}" ]; then 311 echo "SKIP: directory ${NETCONS_CONFIGFS} does not exist. Check if NETCONSOLE_DYNAMIC is enabled" >&2 312 exit "${ksft_skip}" 313 fi 314 315 if ip link show "${DSTIF}" 2> /dev/null; then 316 echo "SKIP: interface ${DSTIF} exists in the system. Not overwriting it." >&2 317 exit "${ksft_skip}" 318 fi 319 320 REGEXP4="inet.*(${SRCIP4}|${DSTIP4})" 321 REGEXP6="inet.*(${SRCIP6}|${DSTIP6})" 322 if ip addr list | grep -E "${REGEXP4}" 2> /dev/null; then 323 echo "SKIP: IPv4s already in use. Skipping it" >&2 324 exit "${ksft_skip}" 325 fi 326 327 if ip addr list | grep -E "${REGEXP6}" 2> /dev/null; then 328 echo "SKIP: IPv6s already in use. Skipping it" >&2 329 exit "${ksft_skip}" 330 fi 331} 332 333function check_for_taskset() { 334 if ! which taskset > /dev/null ; then 335 echo "SKIP: taskset(1) is not available" >&2 336 exit "${ksft_skip}" 337 fi 338} 339 340# This is necessary if running multiple tests in a row 341function pkill_socat() { 342 PROCESS_NAME4="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}" 343 PROCESS_NAME6="socat UDP6-LISTEN:6666,fork ${OUTPUT_FILE}" 344 # socat runs under timeout(1), kill it if it is still alive 345 # do not fail if socat doesn't exist anymore 346 set +e 347 pkill -f "${PROCESS_NAME4}" 348 pkill -f "${PROCESS_NAME6}" 349 set -e 350} 351 352# Check if netconsole was compiled as a module, otherwise exit 353function check_netconsole_module() { 354 if modinfo netconsole | grep filename: | grep -q builtin 355 then 356 echo "SKIP: netconsole should be compiled as a module" >&2 357 exit "${ksft_skip}" 358 fi 359} 360 361# A wrapper to translate protocol version to udp version 362function wait_for_port() { 363 local NAMESPACE=${1} 364 local PORT=${2} 365 IP_VERSION=${3} 366 367 if [ "${IP_VERSION}" == "ipv6" ] 368 then 369 PROTOCOL="udp6" 370 else 371 PROTOCOL="udp" 372 fi 373 374 wait_local_port_listen "${NAMESPACE}" "${PORT}" "${PROTOCOL}" 375 # even after the port is open, let's wait 1 second before writing 376 # otherwise the packet could be missed, and the test will fail. Happens 377 # more frequently on IPv6 378 sleep 1 379} 380