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:-"extended"} 118 119 DSTMAC=$(ip netns exec "${NAMESPACE}" \ 120 ip link show "${DSTIF}" | awk '/ether/ {print $2}') 121 122 # Create a dynamic target 123 mkdir "${NETCONS_PATH}" 124 125 echo "${DSTIP}" > "${NETCONS_PATH}"/remote_ip 126 echo "${SRCIP}" > "${NETCONS_PATH}"/local_ip 127 echo "${DSTMAC}" > "${NETCONS_PATH}"/remote_mac 128 echo "${SRCIF}" > "${NETCONS_PATH}"/dev_name 129 130 if [ "${FORMAT}" == "basic" ] 131 then 132 # Basic target does not support release 133 echo 0 > "${NETCONS_PATH}"/release 134 echo 0 > "${NETCONS_PATH}"/extended 135 elif [ "${FORMAT}" == "extended" ] 136 then 137 echo 1 > "${NETCONS_PATH}"/extended 138 fi 139 140 echo 1 > "${NETCONS_PATH}"/enabled 141 142 # This will make sure that the kernel was able to 143 # load the netconsole driver configuration. The console message 144 # gets more organized/sequential as well. 145 sleep 1 146} 147 148# Generate the command line argument for netconsole following: 149# netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr] 150function create_cmdline_str() { 151 local BINDMODE=${1:-"ifname"} 152 if [ "${BINDMODE}" == "ifname" ] 153 then 154 SRCDEV=${SRCIF} 155 else 156 SRCDEV=$(mac_get "${SRCIF}") 157 fi 158 159 DSTMAC=$(ip netns exec "${NAMESPACE}" \ 160 ip link show "${DSTIF}" | awk '/ether/ {print $2}') 161 SRCPORT="1514" 162 TGTPORT="6666" 163 164 echo "netconsole=\"+${SRCPORT}@${SRCIP}/${SRCDEV},${TGTPORT}@${DSTIP}/${DSTMAC}\"" 165} 166 167# Do not append the release to the header of the message 168function disable_release_append() { 169 echo 0 > "${NETCONS_PATH}"/enabled 170 echo 0 > "${NETCONS_PATH}"/release 171 echo 1 > "${NETCONS_PATH}"/enabled 172} 173 174function do_cleanup() { 175 local NSIM_DEV_SYS_DEL="/sys/bus/netdevsim/del_device" 176 177 # Delete netdevsim devices 178 echo "$NSIM_DEV_2_ID" > "$NSIM_DEV_SYS_DEL" 179 echo "$NSIM_DEV_1_ID" > "$NSIM_DEV_SYS_DEL" 180 181 # this is coming from lib.sh 182 cleanup_all_ns 183 184 # Restoring printk configurations 185 echo "${DEFAULT_PRINTK_VALUES}" > /proc/sys/kernel/printk 186} 187 188function cleanup() { 189 # delete netconsole dynamic reconfiguration 190 echo 0 > "${NETCONS_PATH}"/enabled 191 # Remove all the keys that got created during the selftest 192 find "${NETCONS_PATH}/userdata/" -mindepth 1 -type d -delete 193 # Remove the configfs entry 194 rmdir "${NETCONS_PATH}" 195 196 do_cleanup 197} 198 199function set_user_data() { 200 if [[ ! -d "${NETCONS_PATH}""/userdata" ]] 201 then 202 echo "Userdata path not available in ${NETCONS_PATH}/userdata" 203 exit "${ksft_skip}" 204 fi 205 206 KEY_PATH="${NETCONS_PATH}/userdata/${USERDATA_KEY}" 207 mkdir -p "${KEY_PATH}" 208 VALUE_PATH="${KEY_PATH}""/value" 209 echo "${USERDATA_VALUE}" > "${VALUE_PATH}" 210} 211 212function listen_port_and_save_to() { 213 local OUTPUT=${1} 214 local IPVERSION=${2:-"ipv4"} 215 216 if [ "${IPVERSION}" == "ipv4" ] 217 then 218 SOCAT_MODE="UDP-LISTEN" 219 else 220 SOCAT_MODE="UDP6-LISTEN" 221 fi 222 223 # Just wait for 2 seconds 224 timeout 2 ip netns exec "${NAMESPACE}" \ 225 socat "${SOCAT_MODE}":"${PORT}",fork "${OUTPUT}" 226} 227 228# Only validate that the message arrived properly 229function validate_msg() { 230 local TMPFILENAME="$1" 231 232 # Check if the file exists 233 if [ ! -f "$TMPFILENAME" ]; then 234 echo "FAIL: File was not generated." >&2 235 exit "${ksft_fail}" 236 fi 237 238 if ! grep -q "${MSG}" "${TMPFILENAME}"; then 239 echo "FAIL: ${MSG} not found in ${TMPFILENAME}" >&2 240 cat "${TMPFILENAME}" >&2 241 exit "${ksft_fail}" 242 fi 243} 244 245# Validate the message and userdata 246function validate_result() { 247 local TMPFILENAME="$1" 248 249 # TMPFILENAME will contain something like: 250 # 6.11.1-0_fbk0_rc13_509_g30d75cea12f7,13,1822,115075213798,-;netconsole selftest: netcons_gtJHM 251 # key=value 252 253 validate_msg "${TMPFILENAME}" 254 255 # userdata is not supported on basic format target, 256 # thus, do not validate it. 257 if [ "${FORMAT}" != "basic" ]; 258 then 259 if ! grep -q "${USERDATA_KEY}=${USERDATA_VALUE}" "${TMPFILENAME}"; then 260 echo "FAIL: ${USERDATA_KEY}=${USERDATA_VALUE} not found in ${TMPFILENAME}" >&2 261 cat "${TMPFILENAME}" >&2 262 exit "${ksft_fail}" 263 fi 264 fi 265 266 # Delete the file once it is validated, otherwise keep it 267 # for debugging purposes 268 rm "${TMPFILENAME}" 269} 270 271function check_for_dependencies() { 272 if [ "$(id -u)" -ne 0 ]; then 273 echo "This test must be run as root" >&2 274 exit "${ksft_skip}" 275 fi 276 277 if ! which socat > /dev/null ; then 278 echo "SKIP: socat(1) is not available" >&2 279 exit "${ksft_skip}" 280 fi 281 282 if ! which ip > /dev/null ; then 283 echo "SKIP: ip(1) is not available" >&2 284 exit "${ksft_skip}" 285 fi 286 287 if ! which udevadm > /dev/null ; then 288 echo "SKIP: udevadm(1) is not available" >&2 289 exit "${ksft_skip}" 290 fi 291 292 if [ ! -f /proc/net/if_inet6 ]; then 293 echo "SKIP: IPv6 not configured. Check if CONFIG_IPV6 is enabled" >&2 294 exit "${ksft_skip}" 295 fi 296 297 if [ ! -f "${NSIM_DEV_SYS_NEW}" ]; then 298 echo "SKIP: file ${NSIM_DEV_SYS_NEW} does not exist. Check if CONFIG_NETDEVSIM is enabled" >&2 299 exit "${ksft_skip}" 300 fi 301 302 if [ ! -d "${NETCONS_CONFIGFS}" ]; then 303 echo "SKIP: directory ${NETCONS_CONFIGFS} does not exist. Check if NETCONSOLE_DYNAMIC is enabled" >&2 304 exit "${ksft_skip}" 305 fi 306 307 if ip link show "${DSTIF}" 2> /dev/null; then 308 echo "SKIP: interface ${DSTIF} exists in the system. Not overwriting it." >&2 309 exit "${ksft_skip}" 310 fi 311 312 REGEXP4="inet.*(${SRCIP4}|${DSTIP4})" 313 REGEXP6="inet.*(${SRCIP6}|${DSTIP6})" 314 if ip addr list | grep -E "${REGEXP4}" 2> /dev/null; then 315 echo "SKIP: IPv4s already in use. Skipping it" >&2 316 exit "${ksft_skip}" 317 fi 318 319 if ip addr list | grep -E "${REGEXP6}" 2> /dev/null; then 320 echo "SKIP: IPv6s already in use. Skipping it" >&2 321 exit "${ksft_skip}" 322 fi 323} 324 325function check_for_taskset() { 326 if ! which taskset > /dev/null ; then 327 echo "SKIP: taskset(1) is not available" >&2 328 exit "${ksft_skip}" 329 fi 330} 331 332# This is necessary if running multiple tests in a row 333function pkill_socat() { 334 PROCESS_NAME4="socat UDP-LISTEN:6666,fork ${OUTPUT_FILE}" 335 PROCESS_NAME6="socat UDP6-LISTEN:6666,fork ${OUTPUT_FILE}" 336 # socat runs under timeout(1), kill it if it is still alive 337 # do not fail if socat doesn't exist anymore 338 set +e 339 pkill -f "${PROCESS_NAME4}" 340 pkill -f "${PROCESS_NAME6}" 341 set -e 342} 343 344# Check if netconsole was compiled as a module, otherwise exit 345function check_netconsole_module() { 346 if modinfo netconsole | grep filename: | grep -q builtin 347 then 348 echo "SKIP: netconsole should be compiled as a module" >&2 349 exit "${ksft_skip}" 350 fi 351} 352 353# A wrapper to translate protocol version to udp version 354function wait_for_port() { 355 local NAMESPACE=${1} 356 local PORT=${2} 357 IP_VERSION=${3} 358 359 if [ "${IP_VERSION}" == "ipv6" ] 360 then 361 PROTOCOL="udp6" 362 else 363 PROTOCOL="udp" 364 fi 365 366 wait_local_port_listen "${NAMESPACE}" "${PORT}" "${PROTOCOL}" 367 # even after the port is open, let's wait 1 second before writing 368 # otherwise the packet could be missed, and the test will fail. Happens 369 # more frequently on IPv6 370 sleep 1 371} 372