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