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