1*3d2452c2SBreno Leitao#!/usr/bin/env bash 2*3d2452c2SBreno Leitao# SPDX-License-Identifier: GPL-2.0 3*3d2452c2SBreno Leitao 4*3d2452c2SBreno Leitao# Exercise the netconsole userdata payload. 5*3d2452c2SBreno Leitao# 6*3d2452c2SBreno Leitao# The first part checks that the payload the target transmits follows what 7*3d2452c2SBreno Leitao# configfs says: a value shows up in the next message, an update replaces the 8*3d2452c2SBreno Leitao# previous one, clearing the value drops the entry, and so does removing the 9*3d2452c2SBreno Leitao# key. 10*3d2452c2SBreno Leitao# 11*3d2452c2SBreno Leitao# The second part rewrites values, creates and deletes keys, and clears the 12*3d2452c2SBreno Leitao# payload entirely while messages are being sent, so the transmit path keeps 13*3d2452c2SBreno Leitao# picking up payloads that are being replaced underneath it. It runs twice, 14*3d2452c2SBreno Leitao# once with a payload small enough to fit in a single packet and once large 15*3d2452c2SBreno Leitao# enough to be fragmented. 16*3d2452c2SBreno Leitao# 17*3d2452c2SBreno Leitao# Author: Breno Leitao <leitao@debian.org> 18*3d2452c2SBreno Leitao 19*3d2452c2SBreno Leitaoset -euo pipefail 20*3d2452c2SBreno Leitao 21*3d2452c2SBreno LeitaoSCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") 22*3d2452c2SBreno Leitao 23*3d2452c2SBreno Leitaosource "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh 24*3d2452c2SBreno Leitao 25*3d2452c2SBreno Leitao# Number of times each torture worker loops 26*3d2452c2SBreno LeitaoITERATIONS=${1:-200} 27*3d2452c2SBreno Leitao 28*3d2452c2SBreno Leitao# Keys owned by each torture worker. Workers do not share keys, so a failing 29*3d2452c2SBreno Leitao# configfs operation means a real problem and not a lost race. 30*3d2452c2SBreno LeitaoCHURN_KEY="churnkey" 31*3d2452c2SBreno LeitaoTRANSIENT_KEY="transientkey" 32*3d2452c2SBreno Leitao# Number of keys used to push a message past MAX_PRINT_CHUNK 33*3d2452c2SBreno LeitaoBULK_KEYS=8 34*3d2452c2SBreno Leitao 35*3d2452c2SBreno LeitaoUSERDATA_DIR="${NETCONS_PATH}/userdata" 36*3d2452c2SBreno Leitao# Values are capped at MAX_EXTRADATA_VALUE_LEN(200) bytes, so ${BULK_KEYS} 37*3d2452c2SBreno Leitao# entries of this size are enough to force fragmentation 38*3d2452c2SBreno LeitaoLONG_VALUE=$(printf -- 'v%.0s' {1..190}) 39*3d2452c2SBreno Leitao 40*3d2452c2SBreno Leitaofunction write_key() { 41*3d2452c2SBreno Leitao local KEY="${1}" 42*3d2452c2SBreno Leitao local VALUE="${2}" 43*3d2452c2SBreno Leitao 44*3d2452c2SBreno Leitao mkdir -p "${USERDATA_DIR}/${KEY}" 45*3d2452c2SBreno Leitao echo "${VALUE}" > "${USERDATA_DIR}/${KEY}/value" 46*3d2452c2SBreno Leitao} 47*3d2452c2SBreno Leitao 48*3d2452c2SBreno Leitao# Send a single message and capture it on the destination interface 49*3d2452c2SBreno Leitaofunction send_and_capture() { 50*3d2452c2SBreno Leitao rm -f "${OUTPUT_FILE}" 51*3d2452c2SBreno Leitao 52*3d2452c2SBreno Leitao listen_port_and_save_to "${OUTPUT_FILE}" & 53*3d2452c2SBreno Leitao wait_for_port "${NAMESPACE}" "${PORT}" "${IP_VERSION}" 54*3d2452c2SBreno Leitao echo "${MSG}: ${TARGET}" > /dev/kmsg 55*3d2452c2SBreno Leitao busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true 56*3d2452c2SBreno Leitao pkill_socat 57*3d2452c2SBreno Leitao validate_msg "${OUTPUT_FILE}" 58*3d2452c2SBreno Leitao} 59*3d2452c2SBreno Leitao 60*3d2452c2SBreno Leitaofunction expect_in_msg() { 61*3d2452c2SBreno Leitao local WANTED="${1}" 62*3d2452c2SBreno Leitao 63*3d2452c2SBreno Leitao if ! grep -q -- "${WANTED}" "${OUTPUT_FILE}"; then 64*3d2452c2SBreno Leitao echo "FAIL: '${WANTED}' not found in ${OUTPUT_FILE}" >&2 65*3d2452c2SBreno Leitao cat "${OUTPUT_FILE}" >&2 66*3d2452c2SBreno Leitao exit "${ksft_fail}" 67*3d2452c2SBreno Leitao fi 68*3d2452c2SBreno Leitao} 69*3d2452c2SBreno Leitao 70*3d2452c2SBreno Leitaofunction expect_not_in_msg() { 71*3d2452c2SBreno Leitao local UNWANTED="${1}" 72*3d2452c2SBreno Leitao 73*3d2452c2SBreno Leitao if grep -q -- "${UNWANTED}" "${OUTPUT_FILE}"; then 74*3d2452c2SBreno Leitao echo "FAIL: '${UNWANTED}' found in ${OUTPUT_FILE}" >&2 75*3d2452c2SBreno Leitao cat "${OUTPUT_FILE}" >&2 76*3d2452c2SBreno Leitao exit "${ksft_fail}" 77*3d2452c2SBreno Leitao fi 78*3d2452c2SBreno Leitao} 79*3d2452c2SBreno Leitao 80*3d2452c2SBreno Leitao# Every write publishes a new payload and frees the previous one. An empty 81*3d2452c2SBreno Leitao# value is skipped when the payload is formatted, so this also drives the 82*3d2452c2SBreno Leitao# target through having no payload at all. 83*3d2452c2SBreno Leitaofunction churn_value() { 84*3d2452c2SBreno Leitao local i 85*3d2452c2SBreno Leitao 86*3d2452c2SBreno Leitao for i in $(seq "${ITERATIONS}") 87*3d2452c2SBreno Leitao do 88*3d2452c2SBreno Leitao echo "value${i}" > "${USERDATA_DIR}/${CHURN_KEY}/value" 89*3d2452c2SBreno Leitao echo > "${USERDATA_DIR}/${CHURN_KEY}/value" 90*3d2452c2SBreno Leitao done 91*3d2452c2SBreno Leitao} 92*3d2452c2SBreno Leitao 93*3d2452c2SBreno Leitao# Create and delete a key underneath the sender 94*3d2452c2SBreno Leitaofunction churn_key() { 95*3d2452c2SBreno Leitao local i 96*3d2452c2SBreno Leitao 97*3d2452c2SBreno Leitao for i in $(seq "${ITERATIONS}") 98*3d2452c2SBreno Leitao do 99*3d2452c2SBreno Leitao mkdir "${USERDATA_DIR}/${TRANSIENT_KEY}" 100*3d2452c2SBreno Leitao echo "transient${i}" > "${USERDATA_DIR}/${TRANSIENT_KEY}/value" 101*3d2452c2SBreno Leitao rmdir "${USERDATA_DIR}/${TRANSIENT_KEY}" 102*3d2452c2SBreno Leitao done 103*3d2452c2SBreno Leitao} 104*3d2452c2SBreno Leitao 105*3d2452c2SBreno Leitao# Keep the transmit path busy while the payload is being replaced 106*3d2452c2SBreno Leitaofunction send_messages() { 107*3d2452c2SBreno Leitao local i 108*3d2452c2SBreno Leitao 109*3d2452c2SBreno Leitao for i in $(seq "${ITERATIONS}") 110*3d2452c2SBreno Leitao do 111*3d2452c2SBreno Leitao echo "${MSG}: ${TARGET} ${i}" > /dev/kmsg 112*3d2452c2SBreno Leitao done 113*3d2452c2SBreno Leitao} 114*3d2452c2SBreno Leitao 115*3d2452c2SBreno Leitao# Run the workers concurrently and fail if any of them hits an error 116*3d2452c2SBreno Leitaofunction run_workers() { 117*3d2452c2SBreno Leitao local PIDS=() 118*3d2452c2SBreno Leitao local WORKER 119*3d2452c2SBreno Leitao local RET=0 120*3d2452c2SBreno Leitao local PID 121*3d2452c2SBreno Leitao 122*3d2452c2SBreno Leitao for WORKER in "$@" 123*3d2452c2SBreno Leitao do 124*3d2452c2SBreno Leitao "${WORKER}" & 125*3d2452c2SBreno Leitao PIDS+=("$!") 126*3d2452c2SBreno Leitao done 127*3d2452c2SBreno Leitao 128*3d2452c2SBreno Leitao # Reap every worker before reporting a failure, otherwise a surviving 129*3d2452c2SBreno Leitao # worker keeps writing to configfs while the exit trap cleans it up. 130*3d2452c2SBreno Leitao for PID in "${PIDS[@]}" 131*3d2452c2SBreno Leitao do 132*3d2452c2SBreno Leitao wait "${PID}" || RET=1 133*3d2452c2SBreno Leitao done 134*3d2452c2SBreno Leitao 135*3d2452c2SBreno Leitao if [[ "${RET}" -ne 0 ]] 136*3d2452c2SBreno Leitao then 137*3d2452c2SBreno Leitao echo "FAIL: userdata torture worker failed" >&2 138*3d2452c2SBreno Leitao exit "${ksft_fail}" 139*3d2452c2SBreno Leitao fi 140*3d2452c2SBreno Leitao} 141*3d2452c2SBreno Leitao 142*3d2452c2SBreno Leitaofunction create_bulk_keys() { 143*3d2452c2SBreno Leitao local i 144*3d2452c2SBreno Leitao 145*3d2452c2SBreno Leitao for i in $(seq "${BULK_KEYS}") 146*3d2452c2SBreno Leitao do 147*3d2452c2SBreno Leitao write_key "bulk${i}" "${LONG_VALUE}" 148*3d2452c2SBreno Leitao done 149*3d2452c2SBreno Leitao} 150*3d2452c2SBreno Leitao 151*3d2452c2SBreno Leitaofunction delete_bulk_keys() { 152*3d2452c2SBreno Leitao local i 153*3d2452c2SBreno Leitao 154*3d2452c2SBreno Leitao for i in $(seq "${BULK_KEYS}") 155*3d2452c2SBreno Leitao do 156*3d2452c2SBreno Leitao rmdir "${USERDATA_DIR}/bulk${i}" 157*3d2452c2SBreno Leitao done 158*3d2452c2SBreno Leitao} 159*3d2452c2SBreno Leitao 160*3d2452c2SBreno Leitao# ========== # 161*3d2452c2SBreno Leitao# Start here # 162*3d2452c2SBreno Leitao# ========== # 163*3d2452c2SBreno Leitao 164*3d2452c2SBreno Leitaomodprobe netdevsim 2> /dev/null || true 165*3d2452c2SBreno Leitaomodprobe netconsole 2> /dev/null || true 166*3d2452c2SBreno Leitao 167*3d2452c2SBreno LeitaoIP_VERSION="ipv4" 168*3d2452c2SBreno Leitao# The content of kmsg will be saved to the following file 169*3d2452c2SBreno LeitaoOUTPUT_FILE="/tmp/${TARGET}" 170*3d2452c2SBreno Leitao 171*3d2452c2SBreno Leitao# Check for basic system dependency and exit if not found 172*3d2452c2SBreno Leitaocheck_for_dependencies 173*3d2452c2SBreno Leitao# Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5) 174*3d2452c2SBreno Leitaoecho "6 5" > /proc/sys/kernel/printk 175*3d2452c2SBreno Leitao# Remove the namespace, interfaces and netconsole target on exit 176*3d2452c2SBreno Leitaotrap cleanup EXIT 177*3d2452c2SBreno Leitao# Create one namespace and two interfaces 178*3d2452c2SBreno Leitaoset_network "${IP_VERSION}" 179*3d2452c2SBreno Leitao# Create a dynamic target for netconsole 180*3d2452c2SBreno Leitaocreate_dynamic_target 181*3d2452c2SBreno Leitao 182*3d2452c2SBreno Leitao# =================================================== 183*3d2452c2SBreno Leitao# TEST #1 184*3d2452c2SBreno Leitao# A value written to configfs reaches the destination 185*3d2452c2SBreno Leitao# =================================================== 186*3d2452c2SBreno Leitaowrite_key "${USERDATA_KEY}" "first" 187*3d2452c2SBreno Leitaosend_and_capture 188*3d2452c2SBreno Leitaoexpect_in_msg "${USERDATA_KEY}=first" 189*3d2452c2SBreno Leitao 190*3d2452c2SBreno Leitao# =================================================== 191*3d2452c2SBreno Leitao# TEST #2 192*3d2452c2SBreno Leitao# Updating the value replaces the previous payload 193*3d2452c2SBreno Leitao# =================================================== 194*3d2452c2SBreno Leitaowrite_key "${USERDATA_KEY}" "second" 195*3d2452c2SBreno Leitaosend_and_capture 196*3d2452c2SBreno Leitaoexpect_in_msg "${USERDATA_KEY}=second" 197*3d2452c2SBreno Leitaoexpect_not_in_msg "${USERDATA_KEY}=first" 198*3d2452c2SBreno Leitao 199*3d2452c2SBreno Leitao# =================================================== 200*3d2452c2SBreno Leitao# TEST #3 201*3d2452c2SBreno Leitao# Clearing the value drops the entry 202*3d2452c2SBreno Leitao# =================================================== 203*3d2452c2SBreno Leitaoecho > "${USERDATA_DIR}/${USERDATA_KEY}/value" 204*3d2452c2SBreno Leitaosend_and_capture 205*3d2452c2SBreno Leitaoexpect_not_in_msg "${USERDATA_KEY}=" 206*3d2452c2SBreno Leitao 207*3d2452c2SBreno Leitao# =================================================== 208*3d2452c2SBreno Leitao# TEST #4 209*3d2452c2SBreno Leitao# Removing the key drops the entry 210*3d2452c2SBreno Leitao# =================================================== 211*3d2452c2SBreno Leitaowrite_key "${USERDATA_KEY}" "third" 212*3d2452c2SBreno Leitaormdir "${USERDATA_DIR}/${USERDATA_KEY}" 213*3d2452c2SBreno Leitaosend_and_capture 214*3d2452c2SBreno Leitaoexpect_not_in_msg "${USERDATA_KEY}=" 215*3d2452c2SBreno Leitaorm "${OUTPUT_FILE}" 216*3d2452c2SBreno Leitao 217*3d2452c2SBreno Leitao# =================================================== 218*3d2452c2SBreno Leitao# TEST #5 219*3d2452c2SBreno Leitao# Torture the payload while messages are being sent, 220*3d2452c2SBreno Leitao# first unfragmented and then fragmented 221*3d2452c2SBreno Leitao# =================================================== 222*3d2452c2SBreno Leitaowrite_key "${CHURN_KEY}" "${USERDATA_VALUE}" 223*3d2452c2SBreno Leitaorun_workers churn_value churn_key send_messages 224*3d2452c2SBreno Leitao 225*3d2452c2SBreno Leitaocreate_bulk_keys 226*3d2452c2SBreno Leitaorun_workers churn_value churn_key send_messages 227*3d2452c2SBreno Leitaodelete_bulk_keys 228*3d2452c2SBreno Leitao 229*3d2452c2SBreno Leitaoexit "${ksft_pass}" 230