#!/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 <leitao@debian.org>set -euo pipefailSCRIPTDIR=$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")source "${SCRIPTDIR}"/../lib/sh/lib_netcons.sh# Number of times each torture worker loopsITERATIONS=${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_CHUNKBULK_KEYS=8USERDATA_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 fragmentationLONG_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 interfacefunction 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 senderfunction 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 replacedfunction 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 errorfunction 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 || truemodprobe netconsole 2> /dev/null || trueIP_VERSION="ipv4"# The content of kmsg will be saved to the following fileOUTPUT_FILE="/tmp/${TARGET}"# Check for basic system dependency and exit if not foundcheck_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 exittrap cleanup EXIT# Create one namespace and two interfacesset_network "${IP_VERSION}"# Create a dynamic target for netconsolecreate_dynamic_target# ===================================================# TEST #1# A value written to configfs reaches the destination# ===================================================write_key "${USERDATA_KEY}" "first"send_and_captureexpect_in_msg "${USERDATA_KEY}=first"# ===================================================# TEST #2# Updating the value replaces the previous payload# ===================================================write_key "${USERDATA_KEY}" "second"send_and_captureexpect_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_captureexpect_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_captureexpect_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_messagescreate_bulk_keysrun_workers churn_value churn_key send_messagesdelete_bulk_keysexit "${ksft_pass}"
#!/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 <leitao@debian.org>
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}"