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