xref: /linux/tools/testing/selftests/vsock/vmtest.sh (revision 9361bff6bdb743dabe69d71fa1b8be69575d5b0c)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Copyright (c) 2025 Meta Platforms, Inc. and affiliates
5#
6# Dependencies:
7#		* virtme-ng
8#		* busybox-static (used by virtme-ng)
9#		* qemu	(used by virtme-ng)
10#		* socat
11#
12# shellcheck disable=SC2317,SC2119
13
14readonly SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
15readonly KERNEL_CHECKOUT=$(realpath "${SCRIPT_DIR}"/../../../../)
16
17source "${SCRIPT_DIR}"/../kselftest/ktap_helpers.sh
18
19readonly VSOCK_TEST="${SCRIPT_DIR}"/vsock_test
20readonly TEST_GUEST_PORT=51000
21readonly TEST_HOST_PORT=50000
22readonly TEST_HOST_PORT_LISTENER=50001
23readonly SSH_GUEST_PORT=22
24readonly SSH_HOST_PORT=2222
25readonly VSOCK_CID=1234
26readonly WAIT_PERIOD=3
27readonly WAIT_PERIOD_MAX=60
28readonly WAIT_QEMU=5
29readonly PIDFILE_TEMPLATE=/tmp/vsock_vmtest_XXXX.pid
30declare -A PIDFILES
31
32# virtme-ng offers a netdev for ssh when using "--ssh", but we also need a
33# control port forwarded for vsock_test.  Because virtme-ng doesn't support
34# adding an additional port to forward to the device created from "--ssh" and
35# virtme-init mistakenly sets identical IPs to the ssh device and additional
36# devices, we instead opt out of using --ssh, add the device manually, and also
37# add the kernel cmdline options that virtme-init uses to setup the interface.
38readonly QEMU_TEST_PORT_FWD="hostfwd=tcp::${TEST_HOST_PORT}-:${TEST_GUEST_PORT}"
39readonly QEMU_SSH_PORT_FWD="hostfwd=tcp::${SSH_HOST_PORT}-:${SSH_GUEST_PORT}"
40readonly KERNEL_CMDLINE="\
41	virtme.dhcp net.ifnames=0 biosdevname=0 \
42	virtme.ssh virtme_ssh_channel=tcp virtme_ssh_user=$USER \
43"
44readonly LOG=$(mktemp /tmp/vsock_vmtest_XXXX.log)
45readonly TEST_HOME="$(mktemp -d /tmp/vmtest_home_XXXX)"
46readonly SSH_KEY_PATH="${TEST_HOME}"/.ssh/id_ed25519
47
48# Namespace tests must use the ns_ prefix. This is checked in check_netns() and
49# is used to determine if a test needs namespace setup before test execution.
50readonly TEST_NAMES=(
51	vm_server_host_client
52	vm_client_host_server
53	vm_loopback
54	ns_host_vsock_ns_mode_ok
55	ns_host_vsock_child_ns_mode_ok
56	ns_global_same_cid_fails
57	ns_local_same_cid_ok
58	ns_global_local_same_cid_ok
59	ns_local_global_same_cid_ok
60	ns_diff_global_host_connect_to_global_vm_ok
61	ns_diff_global_host_connect_to_local_vm_fails
62	ns_diff_global_vm_connect_to_global_host_ok
63	ns_diff_global_vm_connect_to_local_host_fails
64	ns_diff_local_host_connect_to_local_vm_fails
65	ns_diff_local_vm_connect_to_local_host_fails
66	ns_diff_global_to_local_loopback_local_fails
67	ns_diff_local_to_global_loopback_fails
68	ns_diff_local_to_local_loopback_fails
69	ns_diff_global_to_global_loopback_ok
70	ns_same_local_loopback_ok
71	ns_same_local_host_connect_to_local_vm_ok
72	ns_same_local_vm_connect_to_local_host_ok
73	ns_delete_vm_ok
74	ns_delete_host_ok
75	ns_delete_both_ok
76)
77readonly TEST_DESCS=(
78	# vm_server_host_client
79	"Run vsock_test in server mode on the VM and in client mode on the host."
80
81	# vm_client_host_server
82	"Run vsock_test in client mode on the VM and in server mode on the host."
83
84	# vm_loopback
85	"Run vsock_test using the loopback transport in the VM."
86
87	# ns_host_vsock_ns_mode_ok
88	"Check /proc/sys/net/vsock/ns_mode strings on the host."
89
90	# ns_host_vsock_child_ns_mode_ok
91	"Check /proc/sys/net/vsock/ns_mode is read-only and child_ns_mode is writable."
92
93	# ns_global_same_cid_fails
94	"Check QEMU fails to start two VMs with same CID in two different global namespaces."
95
96	# ns_local_same_cid_ok
97	"Check QEMU successfully starts two VMs with same CID in two different local namespaces."
98
99	# ns_global_local_same_cid_ok
100	"Check QEMU successfully starts one VM in a global ns and then another VM in a local ns with the same CID."
101
102	# ns_local_global_same_cid_ok
103	"Check QEMU successfully starts one VM in a local ns and then another VM in a global ns with the same CID."
104
105	# ns_diff_global_host_connect_to_global_vm_ok
106	"Run vsock_test client in global ns with server in VM in another global ns."
107
108	# ns_diff_global_host_connect_to_local_vm_fails
109	"Run socat to test a process in a global ns fails to connect to a VM in a local ns."
110
111	# ns_diff_global_vm_connect_to_global_host_ok
112	"Run vsock_test client in VM in a global ns with server in another global ns."
113
114	# ns_diff_global_vm_connect_to_local_host_fails
115	"Run socat to test a VM in a global ns fails to connect to a host process in a local ns."
116
117	# ns_diff_local_host_connect_to_local_vm_fails
118	"Run socat to test a host process in a local ns fails to connect to a VM in another local ns."
119
120	# ns_diff_local_vm_connect_to_local_host_fails
121	"Run socat to test a VM in a local ns fails to connect to a host process in another local ns."
122
123	# ns_diff_global_to_local_loopback_local_fails
124	"Run socat to test a loopback vsock in a global ns fails to connect to a vsock in a local ns."
125
126	# ns_diff_local_to_global_loopback_fails
127	"Run socat to test a loopback vsock in a local ns fails to connect to a vsock in a global ns."
128
129	# ns_diff_local_to_local_loopback_fails
130	"Run socat to test a loopback vsock in a local ns fails to connect to a vsock in another local ns."
131
132	# ns_diff_global_to_global_loopback_ok
133	"Run socat to test a loopback vsock in a global ns successfully connects to a vsock in another global ns."
134
135	# ns_same_local_loopback_ok
136	"Run socat to test a loopback vsock in a local ns successfully connects to a vsock in the same ns."
137
138	# ns_same_local_host_connect_to_local_vm_ok
139	"Run vsock_test client in a local ns with server in VM in same ns."
140
141	# ns_same_local_vm_connect_to_local_host_ok
142	"Run vsock_test client in VM in a local ns with server in same ns."
143
144	# ns_delete_vm_ok
145	"Check that deleting the VM's namespace does not break the socket connection"
146
147	# ns_delete_host_ok
148	"Check that deleting the host's namespace does not break the socket connection"
149
150	# ns_delete_both_ok
151	"Check that deleting the VM and host's namespaces does not break the socket connection"
152)
153
154readonly USE_SHARED_VM=(
155	vm_server_host_client
156	vm_client_host_server
157	vm_loopback
158)
159readonly NS_MODES=("local" "global")
160
161VERBOSE=0
162
163usage() {
164	local name
165	local desc
166	local i
167
168	echo
169	echo "$0 [OPTIONS] [TEST]..."
170	echo "If no TEST argument is given, all tests will be run."
171	echo
172	echo "Options"
173	echo "  -b: build the kernel from the current source tree and use it for guest VMs"
174	echo "  -q: set the path to or name of qemu binary"
175	echo "  -v: verbose output"
176	echo
177	echo "Available tests"
178
179	for ((i = 0; i < ${#TEST_NAMES[@]}; i++)); do
180		name=${TEST_NAMES[${i}]}
181		desc=${TEST_DESCS[${i}]}
182		printf "\t%-55s%-35s\n" "${name}" "${desc}"
183	done
184	echo
185
186	exit 1
187}
188
189die() {
190	echo "$*" >&2
191	exit "${KSFT_FAIL}"
192}
193
194check_result() {
195	local rc arg
196
197	rc=$1
198	arg=$2
199
200	cnt_total=$(( cnt_total + 1 ))
201
202	if [[ ${rc} -eq ${KSFT_PASS} ]]; then
203		cnt_pass=$(( cnt_pass + 1 ))
204		echo "ok ${cnt_total} ${arg}"
205	elif [[ ${rc} -eq ${KSFT_SKIP} ]]; then
206		cnt_skip=$(( cnt_skip + 1 ))
207		echo "ok ${cnt_total} ${arg} # SKIP"
208	elif [[ ${rc} -eq ${KSFT_FAIL} ]]; then
209		cnt_fail=$(( cnt_fail + 1 ))
210		echo "not ok ${cnt_total} ${arg} # exit=${rc}"
211	fi
212}
213
214add_namespaces() {
215	ip netns add "global-parent" 2>/dev/null
216	echo "global" | ip netns exec "global-parent" \
217		tee /proc/sys/net/vsock/child_ns_mode &>/dev/null
218	ip netns add "local-parent" 2>/dev/null
219	echo "local" | ip netns exec "local-parent" \
220		tee /proc/sys/net/vsock/child_ns_mode &>/dev/null
221
222	nsenter --net=/var/run/netns/global-parent \
223		ip netns add "global0" 2>/dev/null
224	nsenter --net=/var/run/netns/global-parent \
225		ip netns add "global1" 2>/dev/null
226	nsenter --net=/var/run/netns/local-parent \
227		ip netns add "local0" 2>/dev/null
228	nsenter --net=/var/run/netns/local-parent \
229		ip netns add "local1" 2>/dev/null
230}
231
232init_namespaces() {
233	for mode in "${NS_MODES[@]}"; do
234		# we need lo for qemu port forwarding
235		ip netns exec "${mode}0" ip link set dev lo up
236		ip netns exec "${mode}1" ip link set dev lo up
237	done
238}
239
240del_namespaces() {
241	for mode in "${NS_MODES[@]}"; do
242		ip netns del "${mode}0" &>/dev/null
243		ip netns del "${mode}1" &>/dev/null
244		log_host "removed ns ${mode}0"
245		log_host "removed ns ${mode}1"
246	done
247	ip netns del "global-parent" &>/dev/null
248	ip netns del "local-parent" &>/dev/null
249}
250
251vm_ssh() {
252	local ns_exec
253
254	if [[ "${1}" == init_ns ]]; then
255		ns_exec=""
256	else
257		ns_exec="ip netns exec ${1}"
258	fi
259
260	shift
261
262	${ns_exec} ssh -q \
263		-i "${SSH_KEY_PATH}" \
264		-o UserKnownHostsFile=/dev/null \
265		-o StrictHostKeyChecking=no \
266		-p "${SSH_HOST_PORT}" \
267		localhost "$@"
268
269	return $?
270}
271
272cleanup() {
273	terminate_pidfiles "${!PIDFILES[@]}"
274	del_namespaces
275	rm -rf "${TEST_HOME}"
276}
277
278check_args() {
279	local found
280
281	for arg in "$@"; do
282		found=0
283		for name in "${TEST_NAMES[@]}"; do
284			if [[ "${name}" = "${arg}" ]]; then
285				found=1
286				break
287			fi
288		done
289
290		if [[ "${found}" -eq 0 ]]; then
291			echo "${arg} is not an available test" >&2
292			usage
293		fi
294	done
295
296	for arg in "$@"; do
297		if ! command -v > /dev/null "test_${arg}"; then
298			echo "Test ${arg} not found" >&2
299			usage
300		fi
301	done
302}
303
304check_deps() {
305	for dep in vng ${QEMU} busybox pkill ssh ss socat nsenter; do
306		if [[ ! -x $(command -v "${dep}") ]]; then
307			echo -e "skip:    dependency ${dep} not found!\n"
308			exit "${KSFT_SKIP}"
309		fi
310	done
311
312	if [[ ! -x $(command -v "${VSOCK_TEST}") ]]; then
313		printf "skip:    %s not found!" "${VSOCK_TEST}"
314		printf " Please build the kselftest vsock target.\n"
315		exit "${KSFT_SKIP}"
316	fi
317}
318
319check_netns() {
320	local tname=$1
321
322	# If the test requires NS support, check if NS support exists
323	# using /proc/self/ns
324	if [[ "${tname}" =~ ^ns_ ]] &&
325	   [[ ! -e /proc/self/ns ]]; then
326		log_host "No NS support detected for test ${tname}"
327		return 1
328	fi
329
330	return 0
331}
332
333# Compare MAJOR.MINOR versions numerically. Returns 0 (true) if $1 < $2.
334version_lt() {
335	local -a a=(${1//./ })
336	local -a b=(${2//./ })
337
338	if [[ "${a[0]}" -lt "${b[0]}" ]]; then
339		return 0
340	elif [[ "${a[0]}" -gt "${b[0]}" ]]; then
341		return 1
342	elif [[ "${a[1]}" -lt "${b[1]}" ]]; then
343		return 0
344	fi
345
346	return 1
347}
348
349check_vng() {
350	local version
351
352	version="$(vng --version | awk '{print $2}')"
353
354	# Supported: 1.33, or any version >= 1.36. 1.34 and 1.35 are untested.
355	if [[ "${version}" == "1.33" ]] || ! version_lt "${version}" "1.36"; then
356		return
357	fi
358
359	printf "warning: vng version '%s' has not been tested and may " "${version}" >&2
360	printf "not function properly.\n\tSupported: 1.33 or >= 1.36\n" >&2
361}
362
363check_socat() {
364	local support_string
365
366	support_string="$(socat -V)"
367
368	if [[ "${support_string}" != *"WITH_VSOCK 1"* ]]; then
369		die "err: socat is missing vsock support"
370	fi
371
372	if [[ "${support_string}" != *"WITH_UNIX 1"* ]]; then
373		die "err: socat is missing unix support"
374	fi
375}
376
377handle_build() {
378	if [[ ! "${BUILD}" -eq 1 ]]; then
379		return
380	fi
381
382	if [[ ! -d "${KERNEL_CHECKOUT}" ]]; then
383		echo "-b requires vmtest.sh called from the kernel source tree" >&2
384		exit 1
385	fi
386
387	pushd "${KERNEL_CHECKOUT}" &>/dev/null
388
389	if ! vng --kconfig --config "${SCRIPT_DIR}"/config; then
390		die "failed to generate .config for kernel source tree (${KERNEL_CHECKOUT})"
391	fi
392
393	if ! make -j$(nproc); then
394		die "failed to build kernel from source tree (${KERNEL_CHECKOUT})"
395	fi
396
397	popd &>/dev/null
398}
399
400setup_home() {
401	mkdir -p "$(dirname "${SSH_KEY_PATH}")"
402	ssh-keygen -t ed25519 -f "${SSH_KEY_PATH}" -N "" -q
403	cp "${VSOCK_TEST}" "${TEST_HOME}"/vsock_test
404}
405
406create_pidfile() {
407	local pidfile
408
409	pidfile=$(mktemp "${PIDFILE_TEMPLATE}")
410	PIDFILES["${pidfile}"]=1
411
412	echo "${pidfile}"
413}
414
415terminate_pidfiles() {
416	local pidfile
417
418	for pidfile in "$@"; do
419		if [[ -s "${pidfile}" ]]; then
420			pkill -SIGTERM -F "${pidfile}" > /dev/null 2>&1
421		fi
422
423		if [[ -e "${pidfile}" ]]; then
424			rm -f "${pidfile}"
425		fi
426
427		unset "PIDFILES[${pidfile}]"
428	done
429}
430
431terminate_pids() {
432	local pid
433
434	for pid in "$@"; do
435		kill -SIGTERM "${pid}" &>/dev/null || :
436	done
437}
438
439vng_dry_run() {
440	# WORKAROUND: use setsid to work around a virtme-ng bug where vng hangs
441	# when called from a background process group (e.g., under make
442	# kselftest). vng save/restores terminal settings using tcsetattr(),
443	# which is not allowed for background process groups because the
444	# controlling terminal is owned by the foreground process group. vng is
445	# stopped with SIGTTOU and hangs until kselftest's timer expires.
446	# setsid works around this by launching vng in a new session that has
447	# no controlling terminal, so tcsetattr() succeeds.
448	#
449	# Fixed in 1.41 (https://github.com/arighi/virtme-ng/pull/453).
450
451	if version_lt "$(vng --version | awk '{print $2}')" "1.41"; then
452		setsid -w vng --run "$@" --dry-run &>/dev/null
453	else
454		vng --run "$@" --dry-run &>/dev/null
455	fi
456}
457
458vm_start() {
459	local pidfile=$1
460	local ns=$2
461	local logfile=/dev/null
462	local verbose_opt=""
463	local kernel_opt=""
464	local qemu_opts=""
465	local ns_exec=""
466	local qemu
467
468	qemu=$(command -v "${QEMU}")
469
470	if [[ "${VERBOSE}" -eq 1 ]]; then
471		verbose_opt="--verbose"
472		logfile=/dev/stdout
473	fi
474
475	qemu_opts="\
476		 -netdev user,id=n0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \
477		 -device virtio-net-pci,netdev=n0 \
478		 -device vhost-vsock-pci,guest-cid=${VSOCK_CID} \
479		--pidfile ${pidfile}
480	"
481
482	if [[ "${BUILD}" -eq 1 ]]; then
483		kernel_opt="${KERNEL_CHECKOUT}"
484	elif vng_dry_run; then
485		kernel_opt=""
486	elif vng_dry_run "${KERNEL_CHECKOUT}"; then
487		kernel_opt="${KERNEL_CHECKOUT}"
488	else
489		die "No suitable kernel found"
490	fi
491
492	if [[ "${ns}" != "init_ns" ]]; then
493		ns_exec="ip netns exec ${ns}"
494	fi
495
496	${ns_exec} vng \
497		--run \
498		${kernel_opt} \
499		${verbose_opt} \
500		--rwdir=/root="${TEST_HOME}" \
501		--force-9p \
502		--cwd /root \
503		--qemu-opts="${qemu_opts}" \
504		--qemu="${qemu}" \
505		--user root \
506		--append "${KERNEL_CMDLINE}" \
507		&> ${logfile} &
508
509	timeout "${WAIT_QEMU}" \
510		bash -c 'while [[ ! -s '"${pidfile}"' ]]; do sleep 1; done; exit 0'
511}
512
513vm_wait_for_ssh() {
514	local ns=$1
515	local i
516
517	i=0
518	while true; do
519		if [[ ${i} -gt ${WAIT_PERIOD_MAX} ]]; then
520			die "Timed out waiting for guest ssh"
521		fi
522
523		if vm_ssh "${ns}" -- true; then
524			break
525		fi
526		i=$(( i + 1 ))
527		sleep ${WAIT_PERIOD}
528	done
529}
530
531# derived from selftests/net/net_helper.sh
532wait_for_listener()
533{
534	local port=$1
535	local interval=$2
536	local max_intervals=$3
537	local protocol=$4
538	local i
539
540	for i in $(seq "${max_intervals}"); do
541		case "${protocol}" in
542		tcp)
543			if ss --listening --tcp --numeric | grep -q ":${port} "; then
544				break
545			fi
546			;;
547		vsock)
548			if ss --listening --vsock --numeric | grep -q ":${port} "; then
549				break
550			fi
551			;;
552		unix)
553			# For unix sockets, port is actually the socket path
554			if ss --listening --unix | grep -q "${port}"; then
555				break
556			fi
557			;;
558		*)
559			echo "Unknown protocol: ${protocol}" >&2
560			break
561			;;
562		esac
563		sleep "${interval}"
564	done
565}
566
567vm_wait_for_listener() {
568	local ns=$1
569	local port=$2
570	local protocol=$3
571
572	vm_ssh "${ns}" <<EOF
573$(declare -f wait_for_listener)
574wait_for_listener ${port} ${WAIT_PERIOD} ${WAIT_PERIOD_MAX} ${protocol}
575EOF
576}
577
578host_wait_for_listener() {
579	local ns=$1
580	local port=$2
581	local protocol=$3
582
583	if [[ "${ns}" == "init_ns" ]]; then
584		wait_for_listener "${port}" "${WAIT_PERIOD}" "${WAIT_PERIOD_MAX}" "${protocol}"
585	else
586		ip netns exec "${ns}" bash <<-EOF
587			$(declare -f wait_for_listener)
588			wait_for_listener ${port} ${WAIT_PERIOD} ${WAIT_PERIOD_MAX} ${protocol}
589		EOF
590	fi
591}
592
593vm_dmesg_oops_count() {
594	local ns=$1
595
596	vm_ssh "${ns}" -- dmesg 2>/dev/null | grep -c -i 'Oops'
597}
598
599vm_dmesg_warn_count() {
600	local ns=$1
601
602	vm_ssh "${ns}" -- dmesg --level=warn 2>/dev/null | grep -c -i 'vsock'
603}
604
605vm_dmesg_check() {
606	local pidfile=$1
607	local ns=$2
608	local oops_before=$3
609	local warn_before=$4
610	local oops_after warn_after
611
612	oops_after=$(vm_dmesg_oops_count "${ns}")
613	if [[ "${oops_after}" -gt "${oops_before}" ]]; then
614		echo "FAIL: kernel oops detected on vm in ns ${ns}" | log_host
615		return 1
616	fi
617
618	warn_after=$(vm_dmesg_warn_count "${ns}")
619	if [[ "${warn_after}" -gt "${warn_before}" ]]; then
620		echo "FAIL: kernel warning detected on vm in ns ${ns}" | log_host
621		return 1
622	fi
623
624	return 0
625}
626
627vm_vsock_test() {
628	local ns=$1
629	local host=$2
630	local cid=$3
631	local port=$4
632	local rc
633
634	# log output and use pipefail to respect vsock_test errors
635	set -o pipefail
636	if [[ "${host}" != server ]]; then
637		vm_ssh "${ns}" -- ./vsock_test \
638			--mode=client \
639			--control-host="${host}" \
640			--peer-cid="${cid}" \
641			--control-port="${port}" \
642			2>&1 | log_guest
643		rc=$?
644	else
645		vm_ssh "${ns}" -- ./vsock_test \
646			--mode=server \
647			--peer-cid="${cid}" \
648			--control-port="${port}" \
649			2>&1 | log_guest &
650		rc=$?
651
652		if [[ $rc -ne 0 ]]; then
653			set +o pipefail
654			return $rc
655		fi
656
657		vm_wait_for_listener "${ns}" "${port}" "tcp"
658		rc=$?
659	fi
660	set +o pipefail
661
662	return $rc
663}
664
665host_vsock_test() {
666	local ns=$1
667	local host=$2
668	local cid=$3
669	local port=$4
670	shift 4
671	local extra_args=("$@")
672	local rc
673
674	local cmd="${VSOCK_TEST}"
675	if [[ "${ns}" != "init_ns" ]]; then
676		cmd="ip netns exec ${ns} ${cmd}"
677	fi
678
679	# log output and use pipefail to respect vsock_test errors
680	set -o pipefail
681	if [[ "${host}" != server ]]; then
682		${cmd} \
683			--mode=client \
684			--peer-cid="${cid}" \
685			--control-host="${host}" \
686			--control-port="${port}" \
687			"${extra_args[@]}" 2>&1 | log_host
688		rc=$?
689	else
690		${cmd} \
691			--mode=server \
692			--peer-cid="${cid}" \
693			--control-port="${port}" \
694			"${extra_args[@]}" 2>&1 | log_host &
695		rc=$?
696
697		if [[ $rc -ne 0 ]]; then
698			set +o pipefail
699			return $rc
700		fi
701
702		host_wait_for_listener "${ns}" "${port}" "tcp"
703		rc=$?
704	fi
705	set +o pipefail
706
707	return $rc
708}
709
710log() {
711	local redirect
712	local prefix
713
714	if [[ ${VERBOSE} -eq 0 ]]; then
715		redirect=/dev/null
716	else
717		redirect=/dev/stdout
718	fi
719
720	prefix="${LOG_PREFIX:-}"
721
722	if [[ "$#" -eq 0 ]]; then
723		if [[ -n "${prefix}" ]]; then
724			awk -v prefix="${prefix}" '{printf "%s: %s\n", prefix, $0}'
725		else
726			cat
727		fi
728	else
729		if [[ -n "${prefix}" ]]; then
730			echo "${prefix}: " "$@"
731		else
732			echo "$@"
733		fi
734	fi | tee -a "${LOG}" > "${redirect}"
735}
736
737log_host() {
738	LOG_PREFIX=host log "$@"
739}
740
741log_guest() {
742	LOG_PREFIX=guest log "$@"
743}
744
745ns_get_mode() {
746	local ns=$1
747
748	ip netns exec "${ns}" cat /proc/sys/net/vsock/ns_mode 2>/dev/null
749}
750
751test_ns_host_vsock_ns_mode_ok() {
752	for mode in "${NS_MODES[@]}"; do
753		local actual
754
755		actual=$(ns_get_mode "${mode}0")
756		if [[ "${actual}" != "${mode}" ]]; then
757			log_host "expected mode ${mode}, got ${actual}"
758			return "${KSFT_FAIL}"
759		fi
760	done
761
762	return "${KSFT_PASS}"
763}
764
765test_ns_diff_global_host_connect_to_global_vm_ok() {
766	local oops_before warn_before
767	local pids pid pidfile
768	local ns0 ns1 port
769	declare -a pids
770	local unixfile
771	ns0="global0"
772	ns1="global1"
773	port=1234
774	local rc
775
776	init_namespaces
777
778	pidfile="$(create_pidfile)"
779
780	if ! vm_start "${pidfile}" "${ns0}"; then
781		return "${KSFT_FAIL}"
782	fi
783
784	vm_wait_for_ssh "${ns0}"
785	oops_before=$(vm_dmesg_oops_count "${ns0}")
786	warn_before=$(vm_dmesg_warn_count "${ns0}")
787
788	unixfile=$(mktemp -u /tmp/XXXX.sock)
789	ip netns exec "${ns1}" \
790		socat TCP-LISTEN:"${TEST_HOST_PORT}",fork \
791			UNIX-CONNECT:"${unixfile}" &
792	pids+=($!)
793	host_wait_for_listener "${ns1}" "${TEST_HOST_PORT}" "tcp"
794
795	ip netns exec "${ns0}" socat UNIX-LISTEN:"${unixfile}",fork \
796		TCP-CONNECT:localhost:"${TEST_HOST_PORT}" &
797	pids+=($!)
798	host_wait_for_listener "${ns0}" "${unixfile}" "unix"
799
800	vm_vsock_test "${ns0}" "server" 2 "${TEST_GUEST_PORT}"
801	vm_wait_for_listener "${ns0}" "${TEST_GUEST_PORT}" "tcp"
802	host_vsock_test "${ns1}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"
803	rc=$?
804
805	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
806	dmesg_rc=$?
807
808	terminate_pids "${pids[@]}"
809	terminate_pidfiles "${pidfile}"
810
811	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
812		return "${KSFT_FAIL}"
813	fi
814
815	return "${KSFT_PASS}"
816}
817
818test_ns_diff_global_host_connect_to_local_vm_fails() {
819	local oops_before warn_before
820	local ns0="global0"
821	local ns1="local0"
822	local port=12345
823	local dmesg_rc
824	local pidfile
825	local result
826	local pid
827
828	init_namespaces
829
830	outfile=$(mktemp)
831
832	pidfile="$(create_pidfile)"
833	if ! vm_start "${pidfile}" "${ns1}"; then
834		log_host "failed to start vm (cid=${VSOCK_CID}, ns=${ns0})"
835		return "${KSFT_FAIL}"
836	fi
837
838	vm_wait_for_ssh "${ns1}"
839	oops_before=$(vm_dmesg_oops_count "${ns1}")
840	warn_before=$(vm_dmesg_warn_count "${ns1}")
841
842	vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" &
843	vm_wait_for_listener "${ns1}" "${port}" "vsock"
844	echo TEST | ip netns exec "${ns0}" \
845		socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null
846
847	vm_dmesg_check "${pidfile}" "${ns1}" "${oops_before}" "${warn_before}"
848	dmesg_rc=$?
849
850	terminate_pidfiles "${pidfile}"
851	result=$(cat "${outfile}")
852	rm -f "${outfile}"
853
854	if [[ "${result}" == "TEST" ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
855		return "${KSFT_FAIL}"
856	fi
857
858	return "${KSFT_PASS}"
859}
860
861test_ns_diff_global_vm_connect_to_global_host_ok() {
862	local oops_before warn_before
863	local ns0="global0"
864	local ns1="global1"
865	local port=12345
866	local unixfile
867	local dmesg_rc
868	local pidfile
869	local pids
870	local rc
871
872	init_namespaces
873
874	declare -a pids
875
876	log_host "Setup socat bridge from ns ${ns0} to ns ${ns1} over port ${port}"
877
878	unixfile=$(mktemp -u /tmp/XXXX.sock)
879
880	ip netns exec "${ns0}" \
881		socat TCP-LISTEN:"${port}" UNIX-CONNECT:"${unixfile}" &
882	pids+=($!)
883	host_wait_for_listener "${ns0}" "${port}" "tcp"
884
885	ip netns exec "${ns1}" \
886		socat UNIX-LISTEN:"${unixfile}" TCP-CONNECT:127.0.0.1:"${port}" &
887	pids+=($!)
888	host_wait_for_listener "${ns1}" "${unixfile}" "unix"
889
890	log_host "Launching ${VSOCK_TEST} in ns ${ns1}"
891	host_vsock_test "${ns1}" "server" "${VSOCK_CID}" "${port}"
892
893	pidfile="$(create_pidfile)"
894	if ! vm_start "${pidfile}" "${ns0}"; then
895		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
896		terminate_pids "${pids[@]}"
897		rm -f "${unixfile}"
898		return "${KSFT_FAIL}"
899	fi
900
901	vm_wait_for_ssh "${ns0}"
902
903	oops_before=$(vm_dmesg_oops_count "${ns0}")
904	warn_before=$(vm_dmesg_warn_count "${ns0}")
905
906	vm_vsock_test "${ns0}" "10.0.2.2" 2 "${port}"
907	rc=$?
908
909	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
910	dmesg_rc=$?
911
912	terminate_pidfiles "${pidfile}"
913	terminate_pids "${pids[@]}"
914	rm -f "${unixfile}"
915
916	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
917		return "${KSFT_FAIL}"
918	fi
919
920	return "${KSFT_PASS}"
921
922}
923
924test_ns_diff_global_vm_connect_to_local_host_fails() {
925	local ns0="global0"
926	local ns1="local0"
927	local port=12345
928	local oops_before warn_before
929	local dmesg_rc
930	local pidfile
931	local result
932	local pid
933
934	init_namespaces
935
936	log_host "Launching socat in ns ${ns1}"
937	outfile=$(mktemp)
938
939	ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}" &
940	pid=$!
941	host_wait_for_listener "${ns1}" "${port}" "vsock"
942
943	pidfile="$(create_pidfile)"
944	if ! vm_start "${pidfile}" "${ns0}"; then
945		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
946		terminate_pids "${pid}"
947		rm -f "${outfile}"
948		return "${KSFT_FAIL}"
949	fi
950
951	vm_wait_for_ssh "${ns0}"
952
953	oops_before=$(vm_dmesg_oops_count "${ns0}")
954	warn_before=$(vm_dmesg_warn_count "${ns0}")
955
956	vm_ssh "${ns0}" -- \
957		bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_guest
958
959	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
960	dmesg_rc=$?
961
962	terminate_pidfiles "${pidfile}"
963	terminate_pids "${pid}"
964
965	result=$(cat "${outfile}")
966	rm -f "${outfile}"
967
968	if [[ "${result}" != TEST ]] && [[ "${dmesg_rc}" -eq 0 ]]; then
969		return "${KSFT_PASS}"
970	fi
971
972	return "${KSFT_FAIL}"
973}
974
975test_ns_diff_local_host_connect_to_local_vm_fails() {
976	local ns0="local0"
977	local ns1="local1"
978	local port=12345
979	local oops_before warn_before
980	local dmesg_rc
981	local pidfile
982	local result
983	local pid
984
985	init_namespaces
986
987	outfile=$(mktemp)
988
989	pidfile="$(create_pidfile)"
990	if ! vm_start "${pidfile}" "${ns1}"; then
991		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
992		return "${KSFT_FAIL}"
993	fi
994
995	vm_wait_for_ssh "${ns1}"
996	oops_before=$(vm_dmesg_oops_count "${ns1}")
997	warn_before=$(vm_dmesg_warn_count "${ns1}")
998
999	vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" &
1000	vm_wait_for_listener "${ns1}" "${port}" "vsock"
1001
1002	echo TEST | ip netns exec "${ns0}" \
1003		socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null
1004
1005	vm_dmesg_check "${pidfile}" "${ns1}" "${oops_before}" "${warn_before}"
1006	dmesg_rc=$?
1007
1008	terminate_pidfiles "${pidfile}"
1009
1010	result=$(cat "${outfile}")
1011	rm -f "${outfile}"
1012
1013	if [[ "${result}" != TEST ]] && [[ "${dmesg_rc}" -eq 0 ]]; then
1014		return "${KSFT_PASS}"
1015	fi
1016
1017	return "${KSFT_FAIL}"
1018}
1019
1020test_ns_diff_local_vm_connect_to_local_host_fails() {
1021	local oops_before warn_before
1022	local ns0="local0"
1023	local ns1="local1"
1024	local port=12345
1025	local dmesg_rc
1026	local pidfile
1027	local result
1028	local pid
1029
1030	init_namespaces
1031
1032	log_host "Launching socat in ns ${ns1}"
1033	outfile=$(mktemp)
1034	ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}" &
1035	pid=$!
1036	host_wait_for_listener "${ns1}" "${port}" "vsock"
1037
1038	pidfile="$(create_pidfile)"
1039	if ! vm_start "${pidfile}" "${ns0}"; then
1040		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
1041		rm -f "${outfile}"
1042		return "${KSFT_FAIL}"
1043	fi
1044
1045	vm_wait_for_ssh "${ns0}"
1046	oops_before=$(vm_dmesg_oops_count "${ns0}")
1047	warn_before=$(vm_dmesg_warn_count "${ns0}")
1048
1049	vm_ssh "${ns0}" -- \
1050		bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_guest
1051
1052	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
1053	dmesg_rc=$?
1054
1055	terminate_pidfiles "${pidfile}"
1056	terminate_pids "${pid}"
1057
1058	result=$(cat "${outfile}")
1059	rm -f "${outfile}"
1060
1061	if [[ "${result}" != TEST ]] && [[ "${dmesg_rc}" -eq 0 ]]; then
1062		return "${KSFT_PASS}"
1063	fi
1064
1065	return "${KSFT_FAIL}"
1066}
1067
1068__test_loopback_two_netns() {
1069	local ns0=$1
1070	local ns1=$2
1071	local port=12345
1072	local result
1073	local pid
1074
1075	modprobe vsock_loopback &> /dev/null || :
1076
1077	log_host "Launching socat in ns ${ns1}"
1078	outfile=$(mktemp)
1079
1080	ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" 2>/dev/null &
1081	pid=$!
1082	host_wait_for_listener "${ns1}" "${port}" "vsock"
1083
1084	log_host "Launching socat in ns ${ns0}"
1085	echo TEST | ip netns exec "${ns0}" socat STDIN VSOCK-CONNECT:1:"${port}" 2>/dev/null
1086	terminate_pids "${pid}"
1087
1088	result=$(cat "${outfile}")
1089	rm -f "${outfile}"
1090
1091	if [[ "${result}" == TEST ]]; then
1092		return 0
1093	fi
1094
1095	return 1
1096}
1097
1098test_ns_diff_global_to_local_loopback_local_fails() {
1099	init_namespaces
1100
1101	if ! __test_loopback_two_netns "global0" "local0"; then
1102		return "${KSFT_PASS}"
1103	fi
1104
1105	return "${KSFT_FAIL}"
1106}
1107
1108test_ns_diff_local_to_global_loopback_fails() {
1109	init_namespaces
1110
1111	if ! __test_loopback_two_netns "local0" "global0"; then
1112		return "${KSFT_PASS}"
1113	fi
1114
1115	return "${KSFT_FAIL}"
1116}
1117
1118test_ns_diff_local_to_local_loopback_fails() {
1119	init_namespaces
1120
1121	if ! __test_loopback_two_netns "local0" "local1"; then
1122		return "${KSFT_PASS}"
1123	fi
1124
1125	return "${KSFT_FAIL}"
1126}
1127
1128test_ns_diff_global_to_global_loopback_ok() {
1129	init_namespaces
1130
1131	if __test_loopback_two_netns "global0" "global1"; then
1132		return "${KSFT_PASS}"
1133	fi
1134
1135	return "${KSFT_FAIL}"
1136}
1137
1138test_ns_same_local_loopback_ok() {
1139	init_namespaces
1140
1141	if __test_loopback_two_netns "local0" "local0"; then
1142		return "${KSFT_PASS}"
1143	fi
1144
1145	return "${KSFT_FAIL}"
1146}
1147
1148test_ns_same_local_host_connect_to_local_vm_ok() {
1149	local oops_before warn_before
1150	local ns="local0"
1151	local port=1234
1152	local dmesg_rc
1153	local pidfile
1154	local rc
1155
1156	init_namespaces
1157
1158	pidfile="$(create_pidfile)"
1159
1160	if ! vm_start "${pidfile}" "${ns}"; then
1161		return "${KSFT_FAIL}"
1162	fi
1163
1164	vm_wait_for_ssh "${ns}"
1165	oops_before=$(vm_dmesg_oops_count "${ns}")
1166	warn_before=$(vm_dmesg_warn_count "${ns}")
1167
1168	vm_vsock_test "${ns}" "server" 2 "${TEST_GUEST_PORT}"
1169
1170	# Skip test 29 (transport release use-after-free): This test attempts
1171	# binding both G2H and H2G CIDs. Because virtio-vsock (G2H) doesn't
1172	# support local namespaces the test will fail when
1173	# transport_g2h->stream_allow() returns false. This edge case only
1174	# happens for vsock_test in client mode on the host in a local
1175	# namespace. This is a false positive.
1176	host_vsock_test "${ns}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}" --skip=29
1177	rc=$?
1178
1179	vm_dmesg_check "${pidfile}" "${ns}" "${oops_before}" "${warn_before}"
1180	dmesg_rc=$?
1181
1182	terminate_pidfiles "${pidfile}"
1183
1184	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
1185		return "${KSFT_FAIL}"
1186	fi
1187
1188	return "${KSFT_PASS}"
1189}
1190
1191test_ns_same_local_vm_connect_to_local_host_ok() {
1192	local oops_before warn_before
1193	local ns="local0"
1194	local port=1234
1195	local dmesg_rc
1196	local pidfile
1197	local rc
1198
1199	init_namespaces
1200
1201	pidfile="$(create_pidfile)"
1202
1203	if ! vm_start "${pidfile}" "${ns}"; then
1204		return "${KSFT_FAIL}"
1205	fi
1206
1207	vm_wait_for_ssh "${ns}"
1208	oops_before=$(vm_dmesg_oops_count "${ns}")
1209	warn_before=$(vm_dmesg_warn_count "${ns}")
1210
1211	host_vsock_test "${ns}" "server" "${VSOCK_CID}" "${port}"
1212	vm_vsock_test "${ns}" "10.0.2.2" 2 "${port}"
1213	rc=$?
1214
1215	vm_dmesg_check "${pidfile}" "${ns}" "${oops_before}" "${warn_before}"
1216	dmesg_rc=$?
1217
1218	terminate_pidfiles "${pidfile}"
1219
1220	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
1221		return "${KSFT_FAIL}"
1222	fi
1223
1224	return "${KSFT_PASS}"
1225}
1226
1227namespaces_can_boot_same_cid() {
1228	local ns0=$1
1229	local ns1=$2
1230	local pidfile1 pidfile2
1231	local rc
1232
1233	pidfile1="$(create_pidfile)"
1234
1235	# The first VM should be able to start. If it can't then we have
1236	# problems and need to return non-zero.
1237	if ! vm_start "${pidfile1}" "${ns0}"; then
1238		return 1
1239	fi
1240
1241	pidfile2="$(create_pidfile)"
1242	vm_start "${pidfile2}" "${ns1}"
1243	rc=$?
1244	terminate_pidfiles "${pidfile1}" "${pidfile2}"
1245
1246	return "${rc}"
1247}
1248
1249test_ns_global_same_cid_fails() {
1250	init_namespaces
1251
1252	if namespaces_can_boot_same_cid "global0" "global1"; then
1253		return "${KSFT_FAIL}"
1254	fi
1255
1256	return "${KSFT_PASS}"
1257}
1258
1259test_ns_local_global_same_cid_ok() {
1260	init_namespaces
1261
1262	if namespaces_can_boot_same_cid "local0" "global0"; then
1263		return "${KSFT_PASS}"
1264	fi
1265
1266	return "${KSFT_FAIL}"
1267}
1268
1269test_ns_global_local_same_cid_ok() {
1270	init_namespaces
1271
1272	if namespaces_can_boot_same_cid "global0" "local0"; then
1273		return "${KSFT_PASS}"
1274	fi
1275
1276	return "${KSFT_FAIL}"
1277}
1278
1279test_ns_local_same_cid_ok() {
1280	init_namespaces
1281
1282	if namespaces_can_boot_same_cid "local0" "local1"; then
1283		return "${KSFT_PASS}"
1284	fi
1285
1286	return "${KSFT_FAIL}"
1287}
1288
1289test_ns_host_vsock_child_ns_mode_ok() {
1290	local rc="${KSFT_PASS}"
1291
1292	for mode in "${NS_MODES[@]}"; do
1293		local ns="${mode}0"
1294
1295		if echo "${mode}" 2>/dev/null > /proc/sys/net/vsock/ns_mode; then
1296			log_host "ns_mode should be read-only but write succeeded"
1297			rc="${KSFT_FAIL}"
1298			continue
1299		fi
1300
1301		if ! echo "${mode}" | ip netns exec "${ns}" \
1302			tee /proc/sys/net/vsock/child_ns_mode &>/dev/null; then
1303			rc="${KSFT_FAIL}"
1304			continue
1305		fi
1306	done
1307
1308	return "${rc}"
1309}
1310
1311test_vm_server_host_client() {
1312	if ! vm_vsock_test "init_ns" "server" 2 "${TEST_GUEST_PORT}"; then
1313		return "${KSFT_FAIL}"
1314	fi
1315
1316	if ! host_vsock_test "init_ns" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"; then
1317		return "${KSFT_FAIL}"
1318	fi
1319
1320	return "${KSFT_PASS}"
1321}
1322
1323test_vm_client_host_server() {
1324	if ! host_vsock_test "init_ns" "server" "${VSOCK_CID}" "${TEST_HOST_PORT_LISTENER}"; then
1325		return "${KSFT_FAIL}"
1326	fi
1327
1328	if ! vm_vsock_test "init_ns" "10.0.2.2" 2 "${TEST_HOST_PORT_LISTENER}"; then
1329		return "${KSFT_FAIL}"
1330	fi
1331
1332	return "${KSFT_PASS}"
1333}
1334
1335test_vm_loopback() {
1336	local port=60000 # non-forwarded local port
1337
1338	vm_ssh "init_ns" -- modprobe vsock_loopback &> /dev/null || :
1339
1340	if ! vm_vsock_test "init_ns" "server" 1 "${port}"; then
1341		return "${KSFT_FAIL}"
1342	fi
1343
1344
1345	if ! vm_vsock_test "init_ns" "127.0.0.1" 1 "${port}"; then
1346		return "${KSFT_FAIL}"
1347	fi
1348
1349	return "${KSFT_PASS}"
1350}
1351
1352check_ns_delete_doesnt_break_connection() {
1353	local pipefile pidfile outfile
1354	local ns0="global0"
1355	local ns1="global1"
1356	local port=12345
1357	local pids=()
1358	local rc=0
1359
1360	init_namespaces
1361
1362	pidfile="$(create_pidfile)"
1363	if ! vm_start "${pidfile}" "${ns0}"; then
1364		return "${KSFT_FAIL}"
1365	fi
1366	vm_wait_for_ssh "${ns0}"
1367
1368	outfile=$(mktemp)
1369	vm_ssh "${ns0}" -- \
1370		socat VSOCK-LISTEN:"${port}",fork STDOUT > "${outfile}" 2>/dev/null &
1371	pids+=($!)
1372	vm_wait_for_listener "${ns0}" "${port}" "vsock"
1373
1374	# We use a pipe here so that we can echo into the pipe instead of using
1375	# socat and a unix socket file. We just need a name for the pipe (not a
1376	# regular file) so use -u.
1377	pipefile=$(mktemp -u /tmp/vmtest_pipe_XXXX)
1378	ip netns exec "${ns1}" \
1379		socat PIPE:"${pipefile}" VSOCK-CONNECT:"${VSOCK_CID}":"${port}" &
1380	pids+=($!)
1381
1382	timeout "${WAIT_PERIOD}" \
1383		bash -c 'while [[ ! -e '"${pipefile}"' ]]; do sleep 1; done; exit 0'
1384
1385	if [[ "$1" == "vm" ]]; then
1386		ip netns del "${ns0}"
1387	elif [[ "$1" == "host" ]]; then
1388		ip netns del "${ns1}"
1389	elif [[ "$1" == "both" ]]; then
1390		ip netns del "${ns0}"
1391		ip netns del "${ns1}"
1392	fi
1393
1394	echo "TEST" > "${pipefile}"
1395
1396	timeout "${WAIT_PERIOD}" \
1397		bash -c 'while [[ ! -s '"${outfile}"' ]]; do sleep 1; done; exit 0'
1398
1399	if grep -q "TEST" "${outfile}"; then
1400		rc="${KSFT_PASS}"
1401	else
1402		rc="${KSFT_FAIL}"
1403	fi
1404
1405	terminate_pidfiles "${pidfile}"
1406	terminate_pids "${pids[@]}"
1407	rm -f "${outfile}" "${pipefile}"
1408
1409	return "${rc}"
1410}
1411
1412test_ns_delete_vm_ok() {
1413	check_ns_delete_doesnt_break_connection "vm"
1414}
1415
1416test_ns_delete_host_ok() {
1417	check_ns_delete_doesnt_break_connection "host"
1418}
1419
1420test_ns_delete_both_ok() {
1421	check_ns_delete_doesnt_break_connection "both"
1422}
1423
1424shared_vm_test() {
1425	local tname
1426
1427	tname="${1}"
1428
1429	for testname in "${USE_SHARED_VM[@]}"; do
1430		if [[ "${tname}" == "${testname}" ]]; then
1431			return 0
1432		fi
1433	done
1434
1435	return 1
1436}
1437
1438shared_vm_tests_requested() {
1439	for arg in "$@"; do
1440		if shared_vm_test "${arg}"; then
1441			return 0
1442		fi
1443	done
1444
1445	return 1
1446}
1447
1448run_shared_vm_tests() {
1449	local arg
1450
1451	for arg in "$@"; do
1452		if ! shared_vm_test "${arg}"; then
1453			continue
1454		fi
1455
1456		if ! check_netns "${arg}"; then
1457			check_result "${KSFT_SKIP}" "${arg}"
1458			continue
1459		fi
1460
1461		run_shared_vm_test "${arg}"
1462		check_result "$?" "${arg}"
1463	done
1464}
1465
1466run_shared_vm_test() {
1467	local host_oops_cnt_before
1468	local host_warn_cnt_before
1469	local vm_oops_cnt_before
1470	local vm_warn_cnt_before
1471	local host_oops_cnt_after
1472	local host_warn_cnt_after
1473	local vm_oops_cnt_after
1474	local vm_warn_cnt_after
1475	local name
1476	local rc
1477
1478	host_oops_cnt_before=$(dmesg | grep -c -i 'Oops')
1479	host_warn_cnt_before=$(dmesg --level=warn | grep -c -i 'vsock')
1480	vm_oops_cnt_before=$(vm_dmesg_oops_count "init_ns")
1481	vm_warn_cnt_before=$(vm_dmesg_warn_count "init_ns")
1482
1483	name=$(echo "${1}" | awk '{ print $1 }')
1484	eval test_"${name}"
1485	rc=$?
1486
1487	host_oops_cnt_after=$(dmesg | grep -i 'Oops' | wc -l)
1488	if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then
1489		echo "FAIL: kernel oops detected on host" | log_host
1490		rc=$KSFT_FAIL
1491	fi
1492
1493	host_warn_cnt_after=$(dmesg --level=warn | grep -c -i 'vsock')
1494	if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then
1495		echo "FAIL: kernel warning detected on host" | log_host
1496		rc=$KSFT_FAIL
1497	fi
1498
1499	vm_oops_cnt_after=$(vm_dmesg_oops_count "init_ns")
1500	if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then
1501		echo "FAIL: kernel oops detected on vm" | log_host
1502		rc=$KSFT_FAIL
1503	fi
1504
1505	vm_warn_cnt_after=$(vm_dmesg_warn_count "init_ns")
1506	if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then
1507		echo "FAIL: kernel warning detected on vm" | log_host
1508		rc=$KSFT_FAIL
1509	fi
1510
1511	return "${rc}"
1512}
1513
1514run_ns_tests() {
1515	for arg in "${ARGS[@]}"; do
1516		if shared_vm_test "${arg}"; then
1517			continue
1518		fi
1519
1520		if ! check_netns "${arg}"; then
1521			check_result "${KSFT_SKIP}" "${arg}"
1522			continue
1523		fi
1524
1525		add_namespaces
1526
1527		name=$(echo "${arg}" | awk '{ print $1 }')
1528		log_host "Executing test_${name}"
1529
1530		host_oops_before=$(dmesg 2>/dev/null | grep -c -i 'Oops')
1531		host_warn_before=$(dmesg --level=warn 2>/dev/null | grep -c -i 'vsock')
1532		eval test_"${name}"
1533		rc=$?
1534
1535		host_oops_after=$(dmesg 2>/dev/null | grep -c -i 'Oops')
1536		if [[ "${host_oops_after}" -gt "${host_oops_before}" ]]; then
1537			echo "FAIL: kernel oops detected on host" | log_host
1538			check_result "${KSFT_FAIL}" "${name}"
1539			del_namespaces
1540			continue
1541		fi
1542
1543		host_warn_after=$(dmesg --level=warn 2>/dev/null | grep -c -i 'vsock')
1544		if [[ "${host_warn_after}" -gt "${host_warn_before}" ]]; then
1545			echo "FAIL: kernel warning detected on host" | log_host
1546			check_result "${KSFT_FAIL}" "${name}"
1547			del_namespaces
1548			continue
1549		fi
1550
1551		check_result "${rc}" "${name}"
1552
1553		del_namespaces
1554	done
1555}
1556
1557BUILD=0
1558QEMU="qemu-system-$(uname -m)"
1559
1560while getopts :hvsq:b o
1561do
1562	case $o in
1563	v) VERBOSE=1;;
1564	b) BUILD=1;;
1565	q) QEMU=$OPTARG;;
1566	h|*) usage;;
1567	esac
1568done
1569shift $((OPTIND-1))
1570
1571trap cleanup EXIT
1572
1573if [[ ${#} -eq 0 ]]; then
1574	ARGS=("${TEST_NAMES[@]}")
1575else
1576	ARGS=("$@")
1577fi
1578
1579check_args "${ARGS[@]}"
1580check_deps
1581check_vng
1582check_socat
1583handle_build
1584setup_home
1585
1586echo "1..${#ARGS[@]}"
1587
1588cnt_pass=0
1589cnt_fail=0
1590cnt_skip=0
1591cnt_total=0
1592
1593if shared_vm_tests_requested "${ARGS[@]}"; then
1594	log_host "Booting up VM"
1595	pidfile="$(create_pidfile)"
1596	vm_start "${pidfile}" "init_ns"
1597	vm_wait_for_ssh "init_ns"
1598	log_host "VM booted up"
1599
1600	run_shared_vm_tests "${ARGS[@]}"
1601	terminate_pidfiles "${pidfile}"
1602fi
1603
1604run_ns_tests "${ARGS[@]}"
1605
1606echo "SUMMARY: PASS=${cnt_pass} SKIP=${cnt_skip} FAIL=${cnt_fail}"
1607echo "Log: ${LOG}"
1608
1609if [ $((cnt_pass + cnt_skip)) -eq ${cnt_total} ]; then
1610	exit "$KSFT_PASS"
1611else
1612	exit "$KSFT_FAIL"
1613fi
1614