#!/usr/bin/env bash # SPDX-License-Identifier: GPL-2.0 # Exercise the netconsole userdata payload. # # The first part checks that the payload the target transmits follows what # configfs says: a value shows up in the next message, an update replaces the # previous one, clearing the value drops the entry, and so does removing the # key. # # The second part rewrites values, creates and deletes keys, and clears the # payload entirely while messages are being sent, so the transmit path keeps # picking up payloads that are being replaced underneath it. It runs twice, # once with a payload small enough to fit in a single packet and once large # enough to be fragmented. # # Author: Breno Leitao set -euo pipefail SCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")") source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh # Number of times each torture worker loops ITERATIONS=${1:-200} # Keys owned by each torture worker. Workers do not share keys, so a failing # configfs operation means a real problem and not a lost race. CHURN_KEY="churnkey" TRANSIENT_KEY="transientkey" # Number of keys used to push a message past MAX_PRINT_CHUNK BULK_KEYS=8 USERDATA_DIR="${NETCONS_PATH}/userdata" # Values are capped at MAX_EXTRADATA_VALUE_LEN(200) bytes, so ${BULK_KEYS} # entries of this size are enough to force fragmentation LONG_VALUE=$(printf -- 'v%.0s' {1..190}) function write_key() { local KEY="${1}" local VALUE="${2}" mkdir -p "${USERDATA_DIR}/${KEY}" echo "${VALUE}" > "${USERDATA_DIR}/${KEY}/value" } # Send a single message and capture it on the destination interface function send_and_capture() { rm -f "${OUTPUT_FILE}" listen_port_and_save_to "${OUTPUT_FILE}" & wait_for_port "${NAMESPACE}" "${PORT}" "${IP_VERSION}" echo "${MSG}: ${TARGET}" > /dev/kmsg busywait "${BUSYWAIT_TIMEOUT}" test -s "${OUTPUT_FILE}" || true pkill_socat validate_msg "${OUTPUT_FILE}" } function expect_in_msg() { local WANTED="${1}" if ! grep -q -- "${WANTED}" "${OUTPUT_FILE}"; then echo "FAIL: '${WANTED}' not found in ${OUTPUT_FILE}" >&2 cat "${OUTPUT_FILE}" >&2 exit "${ksft_fail}" fi } function expect_not_in_msg() { local UNWANTED="${1}" if grep -q -- "${UNWANTED}" "${OUTPUT_FILE}"; then echo "FAIL: '${UNWANTED}' found in ${OUTPUT_FILE}" >&2 cat "${OUTPUT_FILE}" >&2 exit "${ksft_fail}" fi } # Every write publishes a new payload and frees the previous one. An empty # value is skipped when the payload is formatted, so this also drives the # target through having no payload at all. function churn_value() { local i for i in $(seq "${ITERATIONS}") do echo "value${i}" > "${USERDATA_DIR}/${CHURN_KEY}/value" echo > "${USERDATA_DIR}/${CHURN_KEY}/value" done } # Create and delete a key underneath the sender function churn_key() { local i for i in $(seq "${ITERATIONS}") do mkdir "${USERDATA_DIR}/${TRANSIENT_KEY}" echo "transient${i}" > "${USERDATA_DIR}/${TRANSIENT_KEY}/value" rmdir "${USERDATA_DIR}/${TRANSIENT_KEY}" done } # Keep the transmit path busy while the payload is being replaced function send_messages() { local i for i in $(seq "${ITERATIONS}") do echo "${MSG}: ${TARGET} ${i}" > /dev/kmsg done } # Run the workers concurrently and fail if any of them hits an error function run_workers() { local PIDS=() local WORKER local RET=0 local PID for WORKER in "$@" do "${WORKER}" & PIDS+=("$!") done # Reap every worker before reporting a failure, otherwise a surviving # worker keeps writing to configfs while the exit trap cleans it up. for PID in "${PIDS[@]}" do wait "${PID}" || RET=1 done if [[ "${RET}" -ne 0 ]] then echo "FAIL: userdata torture worker failed" >&2 exit "${ksft_fail}" fi } function create_bulk_keys() { local i for i in $(seq "${BULK_KEYS}") do write_key "bulk${i}" "${LONG_VALUE}" done } function delete_bulk_keys() { local i for i in $(seq "${BULK_KEYS}") do rmdir "${USERDATA_DIR}/bulk${i}" done } # ========== # # Start here # # ========== # modprobe netdevsim 2> /dev/null || true modprobe netconsole 2> /dev/null || true IP_VERSION="ipv4" # The content of kmsg will be saved to the following file OUTPUT_FILE="/tmp/${TARGET}" # Check for basic system dependency and exit if not found check_for_dependencies # Set current loglevel to KERN_INFO(6), and default to KERN_NOTICE(5) echo "6 5" > /proc/sys/kernel/printk # Remove the namespace, interfaces and netconsole target on exit trap cleanup EXIT # Create one namespace and two interfaces set_network "${IP_VERSION}" # Create a dynamic target for netconsole create_dynamic_target # =================================================== # TEST #1 # A value written to configfs reaches the destination # =================================================== write_key "${USERDATA_KEY}" "first" send_and_capture expect_in_msg "${USERDATA_KEY}=first" # =================================================== # TEST #2 # Updating the value replaces the previous payload # =================================================== write_key "${USERDATA_KEY}" "second" send_and_capture expect_in_msg "${USERDATA_KEY}=second" expect_not_in_msg "${USERDATA_KEY}=first" # =================================================== # TEST #3 # Clearing the value drops the entry # =================================================== echo > "${USERDATA_DIR}/${USERDATA_KEY}/value" send_and_capture expect_not_in_msg "${USERDATA_KEY}=" # =================================================== # TEST #4 # Removing the key drops the entry # =================================================== write_key "${USERDATA_KEY}" "third" rmdir "${USERDATA_DIR}/${USERDATA_KEY}" send_and_capture expect_not_in_msg "${USERDATA_KEY}=" rm "${OUTPUT_FILE}" # =================================================== # TEST #5 # Torture the payload while messages are being sent, # first unfragmented and then fragmented # =================================================== write_key "${CHURN_KEY}" "${USERDATA_VALUE}" run_workers churn_value churn_key send_messages create_bulk_keys run_workers churn_value churn_key send_messages delete_bulk_keys exit "${ksft_pass}"