xref: /linux/tools/testing/selftests/vsock/vmtest.sh (revision 32e940f2bd3b16551f23ea44be47f6f5d1746d64)
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
333check_vng() {
334	local tested_versions
335	local version
336	local ok
337
338	tested_versions=("1.33" "1.36" "1.37")
339	version="$(vng --version)"
340
341	ok=0
342	for tv in "${tested_versions[@]}"; do
343		if [[ "${version}" == *"${tv}"* ]]; then
344			ok=1
345			break
346		fi
347	done
348
349	if [[ ! "${ok}" -eq 1 ]]; then
350		printf "warning: vng version '%s' has not been tested and may " "${version}" >&2
351		printf "not function properly.\n\tThe following versions have been tested: " >&2
352		echo "${tested_versions[@]}" >&2
353	fi
354}
355
356check_socat() {
357	local support_string
358
359	support_string="$(socat -V)"
360
361	if [[ "${support_string}" != *"WITH_VSOCK 1"* ]]; then
362		die "err: socat is missing vsock support"
363	fi
364
365	if [[ "${support_string}" != *"WITH_UNIX 1"* ]]; then
366		die "err: socat is missing unix support"
367	fi
368}
369
370handle_build() {
371	if [[ ! "${BUILD}" -eq 1 ]]; then
372		return
373	fi
374
375	if [[ ! -d "${KERNEL_CHECKOUT}" ]]; then
376		echo "-b requires vmtest.sh called from the kernel source tree" >&2
377		exit 1
378	fi
379
380	pushd "${KERNEL_CHECKOUT}" &>/dev/null
381
382	if ! vng --kconfig --config "${SCRIPT_DIR}"/config; then
383		die "failed to generate .config for kernel source tree (${KERNEL_CHECKOUT})"
384	fi
385
386	if ! make -j$(nproc); then
387		die "failed to build kernel from source tree (${KERNEL_CHECKOUT})"
388	fi
389
390	popd &>/dev/null
391}
392
393setup_home() {
394	mkdir -p "$(dirname "${SSH_KEY_PATH}")"
395	ssh-keygen -t ed25519 -f "${SSH_KEY_PATH}" -N "" -q
396	cp "${VSOCK_TEST}" "${TEST_HOME}"/vsock_test
397}
398
399create_pidfile() {
400	local pidfile
401
402	pidfile=$(mktemp "${PIDFILE_TEMPLATE}")
403	PIDFILES["${pidfile}"]=1
404
405	echo "${pidfile}"
406}
407
408terminate_pidfiles() {
409	local pidfile
410
411	for pidfile in "$@"; do
412		if [[ -s "${pidfile}" ]]; then
413			pkill -SIGTERM -F "${pidfile}" > /dev/null 2>&1
414		fi
415
416		if [[ -e "${pidfile}" ]]; then
417			rm -f "${pidfile}"
418		fi
419
420		unset "PIDFILES[${pidfile}]"
421	done
422}
423
424terminate_pids() {
425	local pid
426
427	for pid in "$@"; do
428		kill -SIGTERM "${pid}" &>/dev/null || :
429	done
430}
431
432vng_dry_run() {
433	# WORKAROUND: use setsid to work around a virtme-ng bug where vng hangs
434	# when called from a background process group (e.g., under make
435	# kselftest). vng save/restores terminal settings using tcsetattr(),
436	# which is not allowed for background process groups because the
437	# controlling terminal is owned by the foreground process group. vng is
438	# stopped with SIGTTOU and hangs until kselftest's timer expires.
439	# setsid works around this by launching vng in a new session that has
440	# no controlling terminal, so tcsetattr() succeeds.
441
442	setsid -w vng --run "$@" --dry-run &>/dev/null
443}
444
445vm_start() {
446	local pidfile=$1
447	local ns=$2
448	local logfile=/dev/null
449	local verbose_opt=""
450	local kernel_opt=""
451	local qemu_opts=""
452	local ns_exec=""
453	local qemu
454
455	qemu=$(command -v "${QEMU}")
456
457	if [[ "${VERBOSE}" -eq 1 ]]; then
458		verbose_opt="--verbose"
459		logfile=/dev/stdout
460	fi
461
462	qemu_opts="\
463		 -netdev user,id=n0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \
464		 -device virtio-net-pci,netdev=n0 \
465		 -device vhost-vsock-pci,guest-cid=${VSOCK_CID} \
466		--pidfile ${pidfile}
467	"
468
469	if [[ "${BUILD}" -eq 1 ]]; then
470		kernel_opt="${KERNEL_CHECKOUT}"
471	elif vng_dry_run; then
472		kernel_opt=""
473	elif vng_dry_run "${KERNEL_CHECKOUT}"; then
474		kernel_opt="${KERNEL_CHECKOUT}"
475	else
476		die "No suitable kernel found"
477	fi
478
479	if [[ "${ns}" != "init_ns" ]]; then
480		ns_exec="ip netns exec ${ns}"
481	fi
482
483	${ns_exec} vng \
484		--run \
485		${kernel_opt} \
486		${verbose_opt} \
487		--rwdir=/root="${TEST_HOME}" \
488		--force-9p \
489		--cwd /root \
490		--qemu-opts="${qemu_opts}" \
491		--qemu="${qemu}" \
492		--user root \
493		--append "${KERNEL_CMDLINE}" \
494		&> ${logfile} &
495
496	timeout "${WAIT_QEMU}" \
497		bash -c 'while [[ ! -s '"${pidfile}"' ]]; do sleep 1; done; exit 0'
498}
499
500vm_wait_for_ssh() {
501	local ns=$1
502	local i
503
504	i=0
505	while true; do
506		if [[ ${i} -gt ${WAIT_PERIOD_MAX} ]]; then
507			die "Timed out waiting for guest ssh"
508		fi
509
510		if vm_ssh "${ns}" -- true; then
511			break
512		fi
513		i=$(( i + 1 ))
514		sleep ${WAIT_PERIOD}
515	done
516}
517
518# derived from selftests/net/net_helper.sh
519wait_for_listener()
520{
521	local port=$1
522	local interval=$2
523	local max_intervals=$3
524	local protocol=$4
525	local i
526
527	for i in $(seq "${max_intervals}"); do
528		case "${protocol}" in
529		tcp)
530			if ss --listening --tcp --numeric | grep -q ":${port} "; then
531				break
532			fi
533			;;
534		vsock)
535			if ss --listening --vsock --numeric | grep -q ":${port} "; then
536				break
537			fi
538			;;
539		unix)
540			# For unix sockets, port is actually the socket path
541			if ss --listening --unix | grep -q "${port}"; then
542				break
543			fi
544			;;
545		*)
546			echo "Unknown protocol: ${protocol}" >&2
547			break
548			;;
549		esac
550		sleep "${interval}"
551	done
552}
553
554vm_wait_for_listener() {
555	local ns=$1
556	local port=$2
557	local protocol=$3
558
559	vm_ssh "${ns}" <<EOF
560$(declare -f wait_for_listener)
561wait_for_listener ${port} ${WAIT_PERIOD} ${WAIT_PERIOD_MAX} ${protocol}
562EOF
563}
564
565host_wait_for_listener() {
566	local ns=$1
567	local port=$2
568	local protocol=$3
569
570	if [[ "${ns}" == "init_ns" ]]; then
571		wait_for_listener "${port}" "${WAIT_PERIOD}" "${WAIT_PERIOD_MAX}" "${protocol}"
572	else
573		ip netns exec "${ns}" bash <<-EOF
574			$(declare -f wait_for_listener)
575			wait_for_listener ${port} ${WAIT_PERIOD} ${WAIT_PERIOD_MAX} ${protocol}
576		EOF
577	fi
578}
579
580vm_dmesg_oops_count() {
581	local ns=$1
582
583	vm_ssh "${ns}" -- dmesg 2>/dev/null | grep -c -i 'Oops'
584}
585
586vm_dmesg_warn_count() {
587	local ns=$1
588
589	vm_ssh "${ns}" -- dmesg --level=warn 2>/dev/null | grep -c -i 'vsock'
590}
591
592vm_dmesg_check() {
593	local pidfile=$1
594	local ns=$2
595	local oops_before=$3
596	local warn_before=$4
597	local oops_after warn_after
598
599	oops_after=$(vm_dmesg_oops_count "${ns}")
600	if [[ "${oops_after}" -gt "${oops_before}" ]]; then
601		echo "FAIL: kernel oops detected on vm in ns ${ns}" | log_host
602		return 1
603	fi
604
605	warn_after=$(vm_dmesg_warn_count "${ns}")
606	if [[ "${warn_after}" -gt "${warn_before}" ]]; then
607		echo "FAIL: kernel warning detected on vm in ns ${ns}" | log_host
608		return 1
609	fi
610
611	return 0
612}
613
614vm_vsock_test() {
615	local ns=$1
616	local host=$2
617	local cid=$3
618	local port=$4
619	local rc
620
621	# log output and use pipefail to respect vsock_test errors
622	set -o pipefail
623	if [[ "${host}" != server ]]; then
624		vm_ssh "${ns}" -- ./vsock_test \
625			--mode=client \
626			--control-host="${host}" \
627			--peer-cid="${cid}" \
628			--control-port="${port}" \
629			2>&1 | log_guest
630		rc=$?
631	else
632		vm_ssh "${ns}" -- ./vsock_test \
633			--mode=server \
634			--peer-cid="${cid}" \
635			--control-port="${port}" \
636			2>&1 | log_guest &
637		rc=$?
638
639		if [[ $rc -ne 0 ]]; then
640			set +o pipefail
641			return $rc
642		fi
643
644		vm_wait_for_listener "${ns}" "${port}" "tcp"
645		rc=$?
646	fi
647	set +o pipefail
648
649	return $rc
650}
651
652host_vsock_test() {
653	local ns=$1
654	local host=$2
655	local cid=$3
656	local port=$4
657	shift 4
658	local extra_args=("$@")
659	local rc
660
661	local cmd="${VSOCK_TEST}"
662	if [[ "${ns}" != "init_ns" ]]; then
663		cmd="ip netns exec ${ns} ${cmd}"
664	fi
665
666	# log output and use pipefail to respect vsock_test errors
667	set -o pipefail
668	if [[ "${host}" != server ]]; then
669		${cmd} \
670			--mode=client \
671			--peer-cid="${cid}" \
672			--control-host="${host}" \
673			--control-port="${port}" \
674			"${extra_args[@]}" 2>&1 | log_host
675		rc=$?
676	else
677		${cmd} \
678			--mode=server \
679			--peer-cid="${cid}" \
680			--control-port="${port}" \
681			"${extra_args[@]}" 2>&1 | log_host &
682		rc=$?
683
684		if [[ $rc -ne 0 ]]; then
685			set +o pipefail
686			return $rc
687		fi
688
689		host_wait_for_listener "${ns}" "${port}" "tcp"
690		rc=$?
691	fi
692	set +o pipefail
693
694	return $rc
695}
696
697log() {
698	local redirect
699	local prefix
700
701	if [[ ${VERBOSE} -eq 0 ]]; then
702		redirect=/dev/null
703	else
704		redirect=/dev/stdout
705	fi
706
707	prefix="${LOG_PREFIX:-}"
708
709	if [[ "$#" -eq 0 ]]; then
710		if [[ -n "${prefix}" ]]; then
711			awk -v prefix="${prefix}" '{printf "%s: %s\n", prefix, $0}'
712		else
713			cat
714		fi
715	else
716		if [[ -n "${prefix}" ]]; then
717			echo "${prefix}: " "$@"
718		else
719			echo "$@"
720		fi
721	fi | tee -a "${LOG}" > "${redirect}"
722}
723
724log_host() {
725	LOG_PREFIX=host log "$@"
726}
727
728log_guest() {
729	LOG_PREFIX=guest log "$@"
730}
731
732ns_get_mode() {
733	local ns=$1
734
735	ip netns exec "${ns}" cat /proc/sys/net/vsock/ns_mode 2>/dev/null
736}
737
738test_ns_host_vsock_ns_mode_ok() {
739	for mode in "${NS_MODES[@]}"; do
740		local actual
741
742		actual=$(ns_get_mode "${mode}0")
743		if [[ "${actual}" != "${mode}" ]]; then
744			log_host "expected mode ${mode}, got ${actual}"
745			return "${KSFT_FAIL}"
746		fi
747	done
748
749	return "${KSFT_PASS}"
750}
751
752test_ns_diff_global_host_connect_to_global_vm_ok() {
753	local oops_before warn_before
754	local pids pid pidfile
755	local ns0 ns1 port
756	declare -a pids
757	local unixfile
758	ns0="global0"
759	ns1="global1"
760	port=1234
761	local rc
762
763	init_namespaces
764
765	pidfile="$(create_pidfile)"
766
767	if ! vm_start "${pidfile}" "${ns0}"; then
768		return "${KSFT_FAIL}"
769	fi
770
771	vm_wait_for_ssh "${ns0}"
772	oops_before=$(vm_dmesg_oops_count "${ns0}")
773	warn_before=$(vm_dmesg_warn_count "${ns0}")
774
775	unixfile=$(mktemp -u /tmp/XXXX.sock)
776	ip netns exec "${ns1}" \
777		socat TCP-LISTEN:"${TEST_HOST_PORT}",fork \
778			UNIX-CONNECT:"${unixfile}" &
779	pids+=($!)
780	host_wait_for_listener "${ns1}" "${TEST_HOST_PORT}" "tcp"
781
782	ip netns exec "${ns0}" socat UNIX-LISTEN:"${unixfile}",fork \
783		TCP-CONNECT:localhost:"${TEST_HOST_PORT}" &
784	pids+=($!)
785	host_wait_for_listener "${ns0}" "${unixfile}" "unix"
786
787	vm_vsock_test "${ns0}" "server" 2 "${TEST_GUEST_PORT}"
788	vm_wait_for_listener "${ns0}" "${TEST_GUEST_PORT}" "tcp"
789	host_vsock_test "${ns1}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"
790	rc=$?
791
792	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
793	dmesg_rc=$?
794
795	terminate_pids "${pids[@]}"
796	terminate_pidfiles "${pidfile}"
797
798	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
799		return "${KSFT_FAIL}"
800	fi
801
802	return "${KSFT_PASS}"
803}
804
805test_ns_diff_global_host_connect_to_local_vm_fails() {
806	local oops_before warn_before
807	local ns0="global0"
808	local ns1="local0"
809	local port=12345
810	local dmesg_rc
811	local pidfile
812	local result
813	local pid
814
815	init_namespaces
816
817	outfile=$(mktemp)
818
819	pidfile="$(create_pidfile)"
820	if ! vm_start "${pidfile}" "${ns1}"; then
821		log_host "failed to start vm (cid=${VSOCK_CID}, ns=${ns0})"
822		return "${KSFT_FAIL}"
823	fi
824
825	vm_wait_for_ssh "${ns1}"
826	oops_before=$(vm_dmesg_oops_count "${ns1}")
827	warn_before=$(vm_dmesg_warn_count "${ns1}")
828
829	vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" &
830	vm_wait_for_listener "${ns1}" "${port}" "vsock"
831	echo TEST | ip netns exec "${ns0}" \
832		socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null
833
834	vm_dmesg_check "${pidfile}" "${ns1}" "${oops_before}" "${warn_before}"
835	dmesg_rc=$?
836
837	terminate_pidfiles "${pidfile}"
838	result=$(cat "${outfile}")
839	rm -f "${outfile}"
840
841	if [[ "${result}" == "TEST" ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
842		return "${KSFT_FAIL}"
843	fi
844
845	return "${KSFT_PASS}"
846}
847
848test_ns_diff_global_vm_connect_to_global_host_ok() {
849	local oops_before warn_before
850	local ns0="global0"
851	local ns1="global1"
852	local port=12345
853	local unixfile
854	local dmesg_rc
855	local pidfile
856	local pids
857	local rc
858
859	init_namespaces
860
861	declare -a pids
862
863	log_host "Setup socat bridge from ns ${ns0} to ns ${ns1} over port ${port}"
864
865	unixfile=$(mktemp -u /tmp/XXXX.sock)
866
867	ip netns exec "${ns0}" \
868		socat TCP-LISTEN:"${port}" UNIX-CONNECT:"${unixfile}" &
869	pids+=($!)
870	host_wait_for_listener "${ns0}" "${port}" "tcp"
871
872	ip netns exec "${ns1}" \
873		socat UNIX-LISTEN:"${unixfile}" TCP-CONNECT:127.0.0.1:"${port}" &
874	pids+=($!)
875	host_wait_for_listener "${ns1}" "${unixfile}" "unix"
876
877	log_host "Launching ${VSOCK_TEST} in ns ${ns1}"
878	host_vsock_test "${ns1}" "server" "${VSOCK_CID}" "${port}"
879
880	pidfile="$(create_pidfile)"
881	if ! vm_start "${pidfile}" "${ns0}"; then
882		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
883		terminate_pids "${pids[@]}"
884		rm -f "${unixfile}"
885		return "${KSFT_FAIL}"
886	fi
887
888	vm_wait_for_ssh "${ns0}"
889
890	oops_before=$(vm_dmesg_oops_count "${ns0}")
891	warn_before=$(vm_dmesg_warn_count "${ns0}")
892
893	vm_vsock_test "${ns0}" "10.0.2.2" 2 "${port}"
894	rc=$?
895
896	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
897	dmesg_rc=$?
898
899	terminate_pidfiles "${pidfile}"
900	terminate_pids "${pids[@]}"
901	rm -f "${unixfile}"
902
903	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
904		return "${KSFT_FAIL}"
905	fi
906
907	return "${KSFT_PASS}"
908
909}
910
911test_ns_diff_global_vm_connect_to_local_host_fails() {
912	local ns0="global0"
913	local ns1="local0"
914	local port=12345
915	local oops_before warn_before
916	local dmesg_rc
917	local pidfile
918	local result
919	local pid
920
921	init_namespaces
922
923	log_host "Launching socat in ns ${ns1}"
924	outfile=$(mktemp)
925
926	ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}" &
927	pid=$!
928	host_wait_for_listener "${ns1}" "${port}" "vsock"
929
930	pidfile="$(create_pidfile)"
931	if ! vm_start "${pidfile}" "${ns0}"; then
932		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
933		terminate_pids "${pid}"
934		rm -f "${outfile}"
935		return "${KSFT_FAIL}"
936	fi
937
938	vm_wait_for_ssh "${ns0}"
939
940	oops_before=$(vm_dmesg_oops_count "${ns0}")
941	warn_before=$(vm_dmesg_warn_count "${ns0}")
942
943	vm_ssh "${ns0}" -- \
944		bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_guest
945
946	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
947	dmesg_rc=$?
948
949	terminate_pidfiles "${pidfile}"
950	terminate_pids "${pid}"
951
952	result=$(cat "${outfile}")
953	rm -f "${outfile}"
954
955	if [[ "${result}" != TEST ]] && [[ "${dmesg_rc}" -eq 0 ]]; then
956		return "${KSFT_PASS}"
957	fi
958
959	return "${KSFT_FAIL}"
960}
961
962test_ns_diff_local_host_connect_to_local_vm_fails() {
963	local ns0="local0"
964	local ns1="local1"
965	local port=12345
966	local oops_before warn_before
967	local dmesg_rc
968	local pidfile
969	local result
970	local pid
971
972	init_namespaces
973
974	outfile=$(mktemp)
975
976	pidfile="$(create_pidfile)"
977	if ! vm_start "${pidfile}" "${ns1}"; then
978		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
979		return "${KSFT_FAIL}"
980	fi
981
982	vm_wait_for_ssh "${ns1}"
983	oops_before=$(vm_dmesg_oops_count "${ns1}")
984	warn_before=$(vm_dmesg_warn_count "${ns1}")
985
986	vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" &
987	vm_wait_for_listener "${ns1}" "${port}" "vsock"
988
989	echo TEST | ip netns exec "${ns0}" \
990		socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null
991
992	vm_dmesg_check "${pidfile}" "${ns1}" "${oops_before}" "${warn_before}"
993	dmesg_rc=$?
994
995	terminate_pidfiles "${pidfile}"
996
997	result=$(cat "${outfile}")
998	rm -f "${outfile}"
999
1000	if [[ "${result}" != TEST ]] && [[ "${dmesg_rc}" -eq 0 ]]; then
1001		return "${KSFT_PASS}"
1002	fi
1003
1004	return "${KSFT_FAIL}"
1005}
1006
1007test_ns_diff_local_vm_connect_to_local_host_fails() {
1008	local oops_before warn_before
1009	local ns0="local0"
1010	local ns1="local1"
1011	local port=12345
1012	local dmesg_rc
1013	local pidfile
1014	local result
1015	local pid
1016
1017	init_namespaces
1018
1019	log_host "Launching socat in ns ${ns1}"
1020	outfile=$(mktemp)
1021	ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}" &
1022	pid=$!
1023	host_wait_for_listener "${ns1}" "${port}" "vsock"
1024
1025	pidfile="$(create_pidfile)"
1026	if ! vm_start "${pidfile}" "${ns0}"; then
1027		log_host "failed to start vm (cid=${cid}, ns=${ns0})"
1028		rm -f "${outfile}"
1029		return "${KSFT_FAIL}"
1030	fi
1031
1032	vm_wait_for_ssh "${ns0}"
1033	oops_before=$(vm_dmesg_oops_count "${ns0}")
1034	warn_before=$(vm_dmesg_warn_count "${ns0}")
1035
1036	vm_ssh "${ns0}" -- \
1037		bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_guest
1038
1039	vm_dmesg_check "${pidfile}" "${ns0}" "${oops_before}" "${warn_before}"
1040	dmesg_rc=$?
1041
1042	terminate_pidfiles "${pidfile}"
1043	terminate_pids "${pid}"
1044
1045	result=$(cat "${outfile}")
1046	rm -f "${outfile}"
1047
1048	if [[ "${result}" != TEST ]] && [[ "${dmesg_rc}" -eq 0 ]]; then
1049		return "${KSFT_PASS}"
1050	fi
1051
1052	return "${KSFT_FAIL}"
1053}
1054
1055__test_loopback_two_netns() {
1056	local ns0=$1
1057	local ns1=$2
1058	local port=12345
1059	local result
1060	local pid
1061
1062	modprobe vsock_loopback &> /dev/null || :
1063
1064	log_host "Launching socat in ns ${ns1}"
1065	outfile=$(mktemp)
1066
1067	ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" 2>/dev/null &
1068	pid=$!
1069	host_wait_for_listener "${ns1}" "${port}" "vsock"
1070
1071	log_host "Launching socat in ns ${ns0}"
1072	echo TEST | ip netns exec "${ns0}" socat STDIN VSOCK-CONNECT:1:"${port}" 2>/dev/null
1073	terminate_pids "${pid}"
1074
1075	result=$(cat "${outfile}")
1076	rm -f "${outfile}"
1077
1078	if [[ "${result}" == TEST ]]; then
1079		return 0
1080	fi
1081
1082	return 1
1083}
1084
1085test_ns_diff_global_to_local_loopback_local_fails() {
1086	init_namespaces
1087
1088	if ! __test_loopback_two_netns "global0" "local0"; then
1089		return "${KSFT_PASS}"
1090	fi
1091
1092	return "${KSFT_FAIL}"
1093}
1094
1095test_ns_diff_local_to_global_loopback_fails() {
1096	init_namespaces
1097
1098	if ! __test_loopback_two_netns "local0" "global0"; then
1099		return "${KSFT_PASS}"
1100	fi
1101
1102	return "${KSFT_FAIL}"
1103}
1104
1105test_ns_diff_local_to_local_loopback_fails() {
1106	init_namespaces
1107
1108	if ! __test_loopback_two_netns "local0" "local1"; then
1109		return "${KSFT_PASS}"
1110	fi
1111
1112	return "${KSFT_FAIL}"
1113}
1114
1115test_ns_diff_global_to_global_loopback_ok() {
1116	init_namespaces
1117
1118	if __test_loopback_two_netns "global0" "global1"; then
1119		return "${KSFT_PASS}"
1120	fi
1121
1122	return "${KSFT_FAIL}"
1123}
1124
1125test_ns_same_local_loopback_ok() {
1126	init_namespaces
1127
1128	if __test_loopback_two_netns "local0" "local0"; then
1129		return "${KSFT_PASS}"
1130	fi
1131
1132	return "${KSFT_FAIL}"
1133}
1134
1135test_ns_same_local_host_connect_to_local_vm_ok() {
1136	local oops_before warn_before
1137	local ns="local0"
1138	local port=1234
1139	local dmesg_rc
1140	local pidfile
1141	local rc
1142
1143	init_namespaces
1144
1145	pidfile="$(create_pidfile)"
1146
1147	if ! vm_start "${pidfile}" "${ns}"; then
1148		return "${KSFT_FAIL}"
1149	fi
1150
1151	vm_wait_for_ssh "${ns}"
1152	oops_before=$(vm_dmesg_oops_count "${ns}")
1153	warn_before=$(vm_dmesg_warn_count "${ns}")
1154
1155	vm_vsock_test "${ns}" "server" 2 "${TEST_GUEST_PORT}"
1156
1157	# Skip test 29 (transport release use-after-free): This test attempts
1158	# binding both G2H and H2G CIDs. Because virtio-vsock (G2H) doesn't
1159	# support local namespaces the test will fail when
1160	# transport_g2h->stream_allow() returns false. This edge case only
1161	# happens for vsock_test in client mode on the host in a local
1162	# namespace. This is a false positive.
1163	host_vsock_test "${ns}" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}" --skip=29
1164	rc=$?
1165
1166	vm_dmesg_check "${pidfile}" "${ns}" "${oops_before}" "${warn_before}"
1167	dmesg_rc=$?
1168
1169	terminate_pidfiles "${pidfile}"
1170
1171	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
1172		return "${KSFT_FAIL}"
1173	fi
1174
1175	return "${KSFT_PASS}"
1176}
1177
1178test_ns_same_local_vm_connect_to_local_host_ok() {
1179	local oops_before warn_before
1180	local ns="local0"
1181	local port=1234
1182	local dmesg_rc
1183	local pidfile
1184	local rc
1185
1186	init_namespaces
1187
1188	pidfile="$(create_pidfile)"
1189
1190	if ! vm_start "${pidfile}" "${ns}"; then
1191		return "${KSFT_FAIL}"
1192	fi
1193
1194	vm_wait_for_ssh "${ns}"
1195	oops_before=$(vm_dmesg_oops_count "${ns}")
1196	warn_before=$(vm_dmesg_warn_count "${ns}")
1197
1198	host_vsock_test "${ns}" "server" "${VSOCK_CID}" "${port}"
1199	vm_vsock_test "${ns}" "10.0.2.2" 2 "${port}"
1200	rc=$?
1201
1202	vm_dmesg_check "${pidfile}" "${ns}" "${oops_before}" "${warn_before}"
1203	dmesg_rc=$?
1204
1205	terminate_pidfiles "${pidfile}"
1206
1207	if [[ "${rc}" -ne 0 ]] || [[ "${dmesg_rc}" -ne 0 ]]; then
1208		return "${KSFT_FAIL}"
1209	fi
1210
1211	return "${KSFT_PASS}"
1212}
1213
1214namespaces_can_boot_same_cid() {
1215	local ns0=$1
1216	local ns1=$2
1217	local pidfile1 pidfile2
1218	local rc
1219
1220	pidfile1="$(create_pidfile)"
1221
1222	# The first VM should be able to start. If it can't then we have
1223	# problems and need to return non-zero.
1224	if ! vm_start "${pidfile1}" "${ns0}"; then
1225		return 1
1226	fi
1227
1228	pidfile2="$(create_pidfile)"
1229	vm_start "${pidfile2}" "${ns1}"
1230	rc=$?
1231	terminate_pidfiles "${pidfile1}" "${pidfile2}"
1232
1233	return "${rc}"
1234}
1235
1236test_ns_global_same_cid_fails() {
1237	init_namespaces
1238
1239	if namespaces_can_boot_same_cid "global0" "global1"; then
1240		return "${KSFT_FAIL}"
1241	fi
1242
1243	return "${KSFT_PASS}"
1244}
1245
1246test_ns_local_global_same_cid_ok() {
1247	init_namespaces
1248
1249	if namespaces_can_boot_same_cid "local0" "global0"; then
1250		return "${KSFT_PASS}"
1251	fi
1252
1253	return "${KSFT_FAIL}"
1254}
1255
1256test_ns_global_local_same_cid_ok() {
1257	init_namespaces
1258
1259	if namespaces_can_boot_same_cid "global0" "local0"; then
1260		return "${KSFT_PASS}"
1261	fi
1262
1263	return "${KSFT_FAIL}"
1264}
1265
1266test_ns_local_same_cid_ok() {
1267	init_namespaces
1268
1269	if namespaces_can_boot_same_cid "local0" "local1"; then
1270		return "${KSFT_PASS}"
1271	fi
1272
1273	return "${KSFT_FAIL}"
1274}
1275
1276test_ns_host_vsock_child_ns_mode_ok() {
1277	local rc="${KSFT_PASS}"
1278
1279	for mode in "${NS_MODES[@]}"; do
1280		local ns="${mode}0"
1281
1282		if echo "${mode}" 2>/dev/null > /proc/sys/net/vsock/ns_mode; then
1283			log_host "ns_mode should be read-only but write succeeded"
1284			rc="${KSFT_FAIL}"
1285			continue
1286		fi
1287
1288		if ! echo "${mode}" | ip netns exec "${ns}" \
1289			tee /proc/sys/net/vsock/child_ns_mode &>/dev/null; then
1290			rc="${KSFT_FAIL}"
1291			continue
1292		fi
1293	done
1294
1295	return "${rc}"
1296}
1297
1298test_vm_server_host_client() {
1299	if ! vm_vsock_test "init_ns" "server" 2 "${TEST_GUEST_PORT}"; then
1300		return "${KSFT_FAIL}"
1301	fi
1302
1303	if ! host_vsock_test "init_ns" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"; then
1304		return "${KSFT_FAIL}"
1305	fi
1306
1307	return "${KSFT_PASS}"
1308}
1309
1310test_vm_client_host_server() {
1311	if ! host_vsock_test "init_ns" "server" "${VSOCK_CID}" "${TEST_HOST_PORT_LISTENER}"; then
1312		return "${KSFT_FAIL}"
1313	fi
1314
1315	if ! vm_vsock_test "init_ns" "10.0.2.2" 2 "${TEST_HOST_PORT_LISTENER}"; then
1316		return "${KSFT_FAIL}"
1317	fi
1318
1319	return "${KSFT_PASS}"
1320}
1321
1322test_vm_loopback() {
1323	local port=60000 # non-forwarded local port
1324
1325	vm_ssh "init_ns" -- modprobe vsock_loopback &> /dev/null || :
1326
1327	if ! vm_vsock_test "init_ns" "server" 1 "${port}"; then
1328		return "${KSFT_FAIL}"
1329	fi
1330
1331
1332	if ! vm_vsock_test "init_ns" "127.0.0.1" 1 "${port}"; then
1333		return "${KSFT_FAIL}"
1334	fi
1335
1336	return "${KSFT_PASS}"
1337}
1338
1339check_ns_delete_doesnt_break_connection() {
1340	local pipefile pidfile outfile
1341	local ns0="global0"
1342	local ns1="global1"
1343	local port=12345
1344	local pids=()
1345	local rc=0
1346
1347	init_namespaces
1348
1349	pidfile="$(create_pidfile)"
1350	if ! vm_start "${pidfile}" "${ns0}"; then
1351		return "${KSFT_FAIL}"
1352	fi
1353	vm_wait_for_ssh "${ns0}"
1354
1355	outfile=$(mktemp)
1356	vm_ssh "${ns0}" -- \
1357		socat VSOCK-LISTEN:"${port}",fork STDOUT > "${outfile}" 2>/dev/null &
1358	pids+=($!)
1359	vm_wait_for_listener "${ns0}" "${port}" "vsock"
1360
1361	# We use a pipe here so that we can echo into the pipe instead of using
1362	# socat and a unix socket file. We just need a name for the pipe (not a
1363	# regular file) so use -u.
1364	pipefile=$(mktemp -u /tmp/vmtest_pipe_XXXX)
1365	ip netns exec "${ns1}" \
1366		socat PIPE:"${pipefile}" VSOCK-CONNECT:"${VSOCK_CID}":"${port}" &
1367	pids+=($!)
1368
1369	timeout "${WAIT_PERIOD}" \
1370		bash -c 'while [[ ! -e '"${pipefile}"' ]]; do sleep 1; done; exit 0'
1371
1372	if [[ "$1" == "vm" ]]; then
1373		ip netns del "${ns0}"
1374	elif [[ "$1" == "host" ]]; then
1375		ip netns del "${ns1}"
1376	elif [[ "$1" == "both" ]]; then
1377		ip netns del "${ns0}"
1378		ip netns del "${ns1}"
1379	fi
1380
1381	echo "TEST" > "${pipefile}"
1382
1383	timeout "${WAIT_PERIOD}" \
1384		bash -c 'while [[ ! -s '"${outfile}"' ]]; do sleep 1; done; exit 0'
1385
1386	if grep -q "TEST" "${outfile}"; then
1387		rc="${KSFT_PASS}"
1388	else
1389		rc="${KSFT_FAIL}"
1390	fi
1391
1392	terminate_pidfiles "${pidfile}"
1393	terminate_pids "${pids[@]}"
1394	rm -f "${outfile}" "${pipefile}"
1395
1396	return "${rc}"
1397}
1398
1399test_ns_delete_vm_ok() {
1400	check_ns_delete_doesnt_break_connection "vm"
1401}
1402
1403test_ns_delete_host_ok() {
1404	check_ns_delete_doesnt_break_connection "host"
1405}
1406
1407test_ns_delete_both_ok() {
1408	check_ns_delete_doesnt_break_connection "both"
1409}
1410
1411shared_vm_test() {
1412	local tname
1413
1414	tname="${1}"
1415
1416	for testname in "${USE_SHARED_VM[@]}"; do
1417		if [[ "${tname}" == "${testname}" ]]; then
1418			return 0
1419		fi
1420	done
1421
1422	return 1
1423}
1424
1425shared_vm_tests_requested() {
1426	for arg in "$@"; do
1427		if shared_vm_test "${arg}"; then
1428			return 0
1429		fi
1430	done
1431
1432	return 1
1433}
1434
1435run_shared_vm_tests() {
1436	local arg
1437
1438	for arg in "$@"; do
1439		if ! shared_vm_test "${arg}"; then
1440			continue
1441		fi
1442
1443		if ! check_netns "${arg}"; then
1444			check_result "${KSFT_SKIP}" "${arg}"
1445			continue
1446		fi
1447
1448		run_shared_vm_test "${arg}"
1449		check_result "$?" "${arg}"
1450	done
1451}
1452
1453run_shared_vm_test() {
1454	local host_oops_cnt_before
1455	local host_warn_cnt_before
1456	local vm_oops_cnt_before
1457	local vm_warn_cnt_before
1458	local host_oops_cnt_after
1459	local host_warn_cnt_after
1460	local vm_oops_cnt_after
1461	local vm_warn_cnt_after
1462	local name
1463	local rc
1464
1465	host_oops_cnt_before=$(dmesg | grep -c -i 'Oops')
1466	host_warn_cnt_before=$(dmesg --level=warn | grep -c -i 'vsock')
1467	vm_oops_cnt_before=$(vm_dmesg_oops_count "init_ns")
1468	vm_warn_cnt_before=$(vm_dmesg_warn_count "init_ns")
1469
1470	name=$(echo "${1}" | awk '{ print $1 }')
1471	eval test_"${name}"
1472	rc=$?
1473
1474	host_oops_cnt_after=$(dmesg | grep -i 'Oops' | wc -l)
1475	if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then
1476		echo "FAIL: kernel oops detected on host" | log_host
1477		rc=$KSFT_FAIL
1478	fi
1479
1480	host_warn_cnt_after=$(dmesg --level=warn | grep -c -i 'vsock')
1481	if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then
1482		echo "FAIL: kernel warning detected on host" | log_host
1483		rc=$KSFT_FAIL
1484	fi
1485
1486	vm_oops_cnt_after=$(vm_dmesg_oops_count "init_ns")
1487	if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then
1488		echo "FAIL: kernel oops detected on vm" | log_host
1489		rc=$KSFT_FAIL
1490	fi
1491
1492	vm_warn_cnt_after=$(vm_dmesg_warn_count "init_ns")
1493	if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then
1494		echo "FAIL: kernel warning detected on vm" | log_host
1495		rc=$KSFT_FAIL
1496	fi
1497
1498	return "${rc}"
1499}
1500
1501run_ns_tests() {
1502	for arg in "${ARGS[@]}"; do
1503		if shared_vm_test "${arg}"; then
1504			continue
1505		fi
1506
1507		if ! check_netns "${arg}"; then
1508			check_result "${KSFT_SKIP}" "${arg}"
1509			continue
1510		fi
1511
1512		add_namespaces
1513
1514		name=$(echo "${arg}" | awk '{ print $1 }')
1515		log_host "Executing test_${name}"
1516
1517		host_oops_before=$(dmesg 2>/dev/null | grep -c -i 'Oops')
1518		host_warn_before=$(dmesg --level=warn 2>/dev/null | grep -c -i 'vsock')
1519		eval test_"${name}"
1520		rc=$?
1521
1522		host_oops_after=$(dmesg 2>/dev/null | grep -c -i 'Oops')
1523		if [[ "${host_oops_after}" -gt "${host_oops_before}" ]]; then
1524			echo "FAIL: kernel oops detected on host" | log_host
1525			check_result "${KSFT_FAIL}" "${name}"
1526			del_namespaces
1527			continue
1528		fi
1529
1530		host_warn_after=$(dmesg --level=warn 2>/dev/null | grep -c -i 'vsock')
1531		if [[ "${host_warn_after}" -gt "${host_warn_before}" ]]; then
1532			echo "FAIL: kernel warning detected on host" | log_host
1533			check_result "${KSFT_FAIL}" "${name}"
1534			del_namespaces
1535			continue
1536		fi
1537
1538		check_result "${rc}" "${name}"
1539
1540		del_namespaces
1541	done
1542}
1543
1544BUILD=0
1545QEMU="qemu-system-$(uname -m)"
1546
1547while getopts :hvsq:b o
1548do
1549	case $o in
1550	v) VERBOSE=1;;
1551	b) BUILD=1;;
1552	q) QEMU=$OPTARG;;
1553	h|*) usage;;
1554	esac
1555done
1556shift $((OPTIND-1))
1557
1558trap cleanup EXIT
1559
1560if [[ ${#} -eq 0 ]]; then
1561	ARGS=("${TEST_NAMES[@]}")
1562else
1563	ARGS=("$@")
1564fi
1565
1566check_args "${ARGS[@]}"
1567check_deps
1568check_vng
1569check_socat
1570handle_build
1571setup_home
1572
1573echo "1..${#ARGS[@]}"
1574
1575cnt_pass=0
1576cnt_fail=0
1577cnt_skip=0
1578cnt_total=0
1579
1580if shared_vm_tests_requested "${ARGS[@]}"; then
1581	log_host "Booting up VM"
1582	pidfile="$(create_pidfile)"
1583	vm_start "${pidfile}" "init_ns"
1584	vm_wait_for_ssh "init_ns"
1585	log_host "VM booted up"
1586
1587	run_shared_vm_tests "${ARGS[@]}"
1588	terminate_pidfiles "${pidfile}"
1589fi
1590
1591run_ns_tests "${ARGS[@]}"
1592
1593echo "SUMMARY: PASS=${cnt_pass} SKIP=${cnt_skip} FAIL=${cnt_fail}"
1594echo "Log: ${LOG}"
1595
1596if [ $((cnt_pass + cnt_skip)) -eq ${cnt_total} ]; then
1597	exit "$KSFT_PASS"
1598else
1599	exit "$KSFT_FAIL"
1600fi
1601