On Tue, Nov 11, 2025 at 10:54:55PM -0800, Bobby Eshleman wrote:
From: Bobby Eshleman bobbyeshleman@meta.com
Add tests to validate namespace correctness using vsock_test and socat. The vsock_test tool is used to validate expected success tests, but socat is used for expected failure tests. socat is used to ensure that connections are rejected outright instead of failing due to some other socket behavior (as tested in vsock_test). Additionally, socat is already required for tunneling TCP traffic from vsock_test. Using only one of the vsock_test tests like 'test_stream_client_close_client' would have yielded a similar result, but doing so wouldn't remove the socat dependency.
Additionally, check for the dependency socat. socat needs special handling beyond just checking if it is on the path because it must be compiled with support for both vsock and unix. The function check_socat() checks that this support exists.
Add more padding to test name printf strings because the tests added in this patch would otherwise overflow.
Signed-off-by: Bobby Eshleman bobbyeshleman@meta.com
Changes in v9:
- consistent variable quoting
tools/testing/selftests/vsock/vmtest.sh | 463 +++++++++++++++++++++++++++++++- 1 file changed, 461 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/vsock/vmtest.sh b/tools/testing/selftests/vsock/vmtest.sh index cc8dc280afdf..111059924287 100755 --- a/tools/testing/selftests/vsock/vmtest.sh +++ b/tools/testing/selftests/vsock/vmtest.sh @@ -7,6 +7,7 @@ # * virtme-ng # * busybox-static (used by virtme-ng) # * qemu (used by virtme-ng) +# * socat # # shellcheck disable=SC2317,SC2119
@@ -52,6 +53,19 @@ readonly TEST_NAMES=( ns_local_same_cid_ok ns_global_local_same_cid_ok ns_local_global_same_cid_ok
- ns_diff_global_host_connect_to_global_vm_ok
- ns_diff_global_host_connect_to_local_vm_fails
- ns_diff_global_vm_connect_to_global_host_ok
- ns_diff_global_vm_connect_to_local_host_fails
- ns_diff_local_host_connect_to_local_vm_fails
- ns_diff_local_vm_connect_to_local_host_fails
- ns_diff_global_to_local_loopback_local_fails
- ns_diff_local_to_global_loopback_fails
- ns_diff_local_to_local_loopback_fails
- ns_diff_global_to_global_loopback_ok
- ns_same_local_loopback_ok
- ns_same_local_host_connect_to_local_vm_ok
- ns_same_local_vm_connect_to_local_host_ok
) readonly TEST_DESCS=( # vm_server_host_client @@ -82,6 +96,45 @@ readonly TEST_DESCS=(
# ns_local_global_same_cid_ok "Check QEMU successfully starts one VM in a local ns and then another VM in a global ns with the same CID."
- # ns_diff_global_host_connect_to_global_vm_ok
- "Run vsock_test client in global ns with server in VM in another global ns."
- # ns_diff_global_host_connect_to_local_vm_fails
- "Run socat to test a process in a global ns fails to connect to a VM in a local ns."
- # ns_diff_global_vm_connect_to_global_host_ok
- "Run vsock_test client in VM in a global ns with server in another global ns."
- # ns_diff_global_vm_connect_to_local_host_fails
- "Run socat to test a VM in a global ns fails to connect to a host process in a local ns."
- # ns_diff_local_host_connect_to_local_vm_fails
- "Run socat to test a host process in a local ns fails to connect to a VM in another local ns."
- # ns_diff_local_vm_connect_to_local_host_fails
- "Run socat to test a VM in a local ns fails to connect to a host process in another local ns."
- # ns_diff_global_to_local_loopback_local_fails
- "Run socat to test a loopback vsock in a global ns fails to connect to a vsock in a local ns."
- # ns_diff_local_to_global_loopback_fails
- "Run socat to test a loopback vsock in a local ns fails to connect to a vsock in a global ns."
- # ns_diff_local_to_local_loopback_fails
- "Run socat to test a loopback vsock in a local ns fails to connect to a vsock in another local ns."
- # ns_diff_global_to_global_loopback_ok
- "Run socat to test a loopback vsock in a global ns successfully connects to a vsock in another global ns."
- # ns_same_local_loopback_ok
- "Run socat to test a loopback vsock in a local ns successfully connects to a vsock in the same ns."
- # ns_same_local_host_connect_to_local_vm_ok
- "Run vsock_test client in a local ns with server in VM in same ns."
- # ns_same_local_vm_connect_to_local_host_ok
- "Run vsock_test client in VM in a local ns with server in same ns."
)
readonly USE_SHARED_VM=( @@ -113,7 +166,7 @@ usage() { for ((i = 0; i < ${#TEST_NAMES[@]}; i++)); do name=${TEST_NAMES[${i}]} desc=${TEST_DESCS[${i}]}
printf "\t%-35s%-35s\n" "${name}" "${desc}"
done echoprintf "\t%-55s%-35s\n" "${name}" "${desc}"@@ -232,7 +285,7 @@ check_args() { }
check_deps() {
- for dep in vng ${QEMU} busybox pkill ssh; do
- for dep in vng ${QEMU} busybox pkill ssh socat; do if [[ ! -x $(command -v "${dep}") ]]; then echo -e "skip: dependency ${dep} not found!\n" exit "${KSFT_SKIP}"
@@ -283,6 +336,20 @@ check_vng() { fi }
+check_socat() {
- local support_string
- support_string="$(socat -V)"
- if [[ "${support_string}" != *"WITH_VSOCK 1"* ]]; then
die "err: socat is missing vsock support"- fi
- if [[ "${support_string}" != *"WITH_UNIX 1"* ]]; then
die "err: socat is missing unix support"- fi
+}
handle_build() { if [[ ! "${BUILD}" -eq 1 ]]; then return @@ -331,6 +398,14 @@ terminate_pidfiles() { done }
+terminate_pids() {
- local pid
- for pid in "$@"; do
kill -SIGTERM "${pid}" &>/dev/null || :- done
+}
vm_start() { local pidfile=$1 local ns=$2 @@ -573,6 +648,389 @@ test_ns_host_vsock_ns_mode_ok() { return "${KSFT_PASS}" }
+test_ns_diff_global_host_connect_to_global_vm_ok() {
- local pids pid pidfile
- local ns0 ns1 port
- declare -a pids
- local unixfile
- ns0="global0"
- ns1="global1"
- port=1234
- local rc
- init_namespaces
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns0}"; then
return "${KSFT_FAIL}"- fi
- unixfile=$(mktemp -u /tmp/XXXX.sock)
- ip netns exec "${ns1}" \
socat TCP-LISTEN:"${TEST_HOST_PORT}",fork \UNIX-CONNECT:"${unixfile}" &- pids+=($!)
- host_wait_for_listener "${ns1}" "${TEST_HOST_PORT}"
- ip netns exec "${ns0}" socat UNIX-LISTEN:"${unixfile}",fork \
TCP-CONNECT:localhost:"${TEST_HOST_PORT}" &- pids+=($!)
- vm_vsock_test "${ns0}" "server" 2 "${TEST_GUEST_PORT}"
- vm_wait_for_listener "${ns0}" "${TEST_GUEST_PORT}"
- host_vsock_test "${ns1}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"
- rc=$?
- for pid in "${pids[@]}"; do
if [[ "$(jobs -p)" = *"${pid}"* ]]; thenkill -SIGTERM "${pid}" &>/dev/nullfi- done
In run_shared_vm_test() we are also checking oops, warn in both host and VM, should we do the same here in each no-shared test that boot a VM?
I mean, should we generalize run_shared_vm_test() and use it for both kind of tests?
Stefano
- terminate_pidfiles "${pidfile}"
- if [[ "${rc}" -ne 0 ]]; then
return "${KSFT_FAIL}"- fi
- return "${KSFT_PASS}"
+}
+test_ns_diff_global_host_connect_to_local_vm_fails() {
- local ns0="global0"
- local ns1="local0"
- local port=12345
- local pidfile
- local result
- local pid
- init_namespaces
- outfile=$(mktemp)
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns1}"; then
log_host "failed to start vm (cid=${VSOCK_CID}, ns=${ns0})"return "${KSFT_FAIL}"- fi
- vm_wait_for_ssh "${ns1}"
- vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" &
- echo TEST | ip netns exec "${ns0}" \
socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null- terminate_pidfiles "${pidfile}"
- result=$(cat "${outfile}")
- rm -f "${outfile}"
- if [[ "${result}" != TEST ]]; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_diff_global_vm_connect_to_global_host_ok() {
- local ns0="global0"
- local ns1="global1"
- local port=12345
- local unixfile
- local pidfile
- local pids
- init_namespaces
- declare -a pids
- log_host "Setup socat bridge from ns ${ns0} to ns ${ns1} over port ${port}"
- unixfile=$(mktemp -u /tmp/XXXX.sock)
- ip netns exec "${ns0}" \
socat TCP-LISTEN:"${port}" UNIX-CONNECT:"${unixfile}" &- pids+=($!)
- ip netns exec "${ns1}" \
socat UNIX-LISTEN:"${unixfile}" TCP-CONNECT:127.0.0.1:"${port}" &- pids+=($!)
- log_host "Launching ${VSOCK_TEST} in ns ${ns1}"
- host_vsock_test "${ns1}" "server" "${VSOCK_CID}" "${port}"
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns0}"; then
log_host "failed to start vm (cid=${cid}, ns=${ns0})"terminate_pids "${pids[@]}"rm -f "${unixfile}"return "${KSFT_FAIL}"- fi
- vm_wait_for_ssh "${ns0}"
- vm_vsock_test "${ns0}" "10.0.2.2" 2 "${port}"
- rc=$?
- terminate_pidfiles "${pidfile}"
- terminate_pids "${pids[@]}"
- rm -f "${unixfile}"
- if [[ ! $rc -eq 0 ]]; then
return "${KSFT_FAIL}"- fi
- return "${KSFT_PASS}"
+}
+test_ns_diff_global_vm_connect_to_local_host_fails() {
- local ns0="global0"
- local ns1="local0"
- local port=12345
- local pidfile
- local result
- local pid
- init_namespaces
- log_host "Launching socat in ns ${ns1}"
- outfile=$(mktemp)
- ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}" &
- pid=$!
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns0}"; then
log_host "failed to start vm (cid=${cid}, ns=${ns0})"terminate_pids "${pid}"rm -f "${outfile}"return "${KSFT_FAIL}"- fi
- vm_wait_for_ssh "${ns0}"
- vm_ssh "${ns0}" -- \
bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_guest- terminate_pidfiles "${pidfile}"
- terminate_pids "${pid}"
- result=$(cat "${outfile}")
- rm -f "${outfile}"
- if [[ "${result}" != TEST ]]; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_diff_local_host_connect_to_local_vm_fails() {
- local ns0="local0"
- local ns1="local1"
- local port=12345
- local pidfile
- local result
- local pid
- init_namespaces
- outfile=$(mktemp)
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns1}"; then
log_host "failed to start vm (cid=${cid}, ns=${ns0})"return "${KSFT_FAIL}"- fi
- vm_wait_for_ssh "${ns1}"
- vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" &
- echo TEST | ip netns exec "${ns0}" \
socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null- terminate_pidfiles "${pidfile}"
- result=$(cat "${outfile}")
- rm -f "${outfile}"
- if [[ "${result}" != TEST ]]; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_diff_local_vm_connect_to_local_host_fails() {
- local ns0="local0"
- local ns1="local1"
- local port=12345
- local pidfile
- local result
- local pid
- init_namespaces
- log_host "Launching socat in ns ${ns1}"
- outfile=$(mktemp)
- ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}" &
- pid=$!
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns0}"; then
log_host "failed to start vm (cid=${cid}, ns=${ns0})"rm -f "${outfile}"return "${KSFT_FAIL}"- fi
- vm_wait_for_ssh "${ns0}"
- vm_ssh "${ns0}" -- \
bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_guest- terminate_pidfiles "${pidfile}"
- terminate_pids "${pid}"
- result=$(cat "${outfile}")
- rm -f "${outfile}"
- if [[ "${result}" != TEST ]]; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+__test_loopback_two_netns() {
- local ns0=$1
- local ns1=$2
- local port=12345
- local result
- local pid
- modprobe vsock_loopback &> /dev/null || :
- log_host "Launching socat in ns ${ns1}"
- outfile=$(mktemp)
- ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" 2>/dev/null &
- pid=$!
- log_host "Launching socat in ns ${ns0}"
- echo TEST | ip netns exec "${ns0}" socat STDIN VSOCK-CONNECT:1:"${port}" 2>/dev/null
- terminate_pids "${pid}"
- result=$(cat "${outfile}")
- rm -f "${outfile}"
- if [[ "${result}" == TEST ]]; then
return 0- fi
- return 1
+}
+test_ns_diff_global_to_local_loopback_local_fails() {
- init_namespaces
- if ! __test_loopback_two_netns "global0" "local0"; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_diff_local_to_global_loopback_fails() {
- init_namespaces
- if ! __test_loopback_two_netns "local0" "global0"; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_diff_local_to_local_loopback_fails() {
- init_namespaces
- if ! __test_loopback_two_netns "local0" "local1"; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_diff_global_to_global_loopback_ok() {
- init_namespaces
- if __test_loopback_two_netns "global0" "global1"; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_same_local_loopback_ok() {
- init_namespaces
- if __test_loopback_two_netns "local0" "local0"; then
return "${KSFT_PASS}"- fi
- return "${KSFT_FAIL}"
+}
+test_ns_same_local_host_connect_to_local_vm_ok() {
- local ns="local0"
- local port=1234
- local pidfile
- local rc
- init_namespaces
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns}"; then
return "${KSFT_FAIL}"- fi
- vm_vsock_test "${ns}" "server" 2 "${TEST_GUEST_PORT}"
- host_vsock_test "${ns}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"
- rc=$?
- terminate_pidfiles "${pidfile}"
- if [[ $rc -ne 0 ]]; then
return "${KSFT_FAIL}"- fi
- return "${KSFT_PASS}"
+}
+test_ns_same_local_vm_connect_to_local_host_ok() {
- local ns="local0"
- local port=1234
- local pidfile
- local rc
- init_namespaces
- pidfile="$(create_pidfile)"
- if ! vm_start "${pidfile}" "${ns}"; then
return "${KSFT_FAIL}"- fi
- vm_vsock_test "${ns}" "server" 2 "${TEST_GUEST_PORT}"
- host_vsock_test "${ns}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"
- rc=$?
- terminate_pidfiles "${pidfile}"
- if [[ $rc -ne 0 ]]; then
return "${KSFT_FAIL}"- fi
- return "${KSFT_PASS}"
+}
namespaces_can_boot_same_cid() { local ns0=$1 local ns1=$2 @@ -851,6 +1309,7 @@ fi check_args "${ARGS[@]}" check_deps check_vng +check_socat handle_build
echo "1..${#ARGS[@]}"
-- 2.47.3