xref: /linux/tools/testing/selftests/vsock/vmtest.sh (revision 06cf7895abf9080c050767c66b95d79d99e2c7e8)
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#
11# shellcheck disable=SC2317,SC2119
12
13readonly SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
14readonly KERNEL_CHECKOUT=$(realpath "${SCRIPT_DIR}"/../../../../)
15
16source "${SCRIPT_DIR}"/../kselftest/ktap_helpers.sh
17
18readonly VSOCK_TEST="${SCRIPT_DIR}"/vsock_test
19readonly TEST_GUEST_PORT=51000
20readonly TEST_HOST_PORT=50000
21readonly TEST_HOST_PORT_LISTENER=50001
22readonly SSH_GUEST_PORT=22
23readonly SSH_HOST_PORT=2222
24readonly VSOCK_CID=1234
25readonly WAIT_PERIOD=3
26readonly WAIT_PERIOD_MAX=60
27readonly WAIT_QEMU=5
28readonly PIDFILE_TEMPLATE=/tmp/vsock_vmtest_XXXX.pid
29declare -A PIDFILES
30
31# virtme-ng offers a netdev for ssh when using "--ssh", but we also need a
32# control port forwarded for vsock_test.  Because virtme-ng doesn't support
33# adding an additional port to forward to the device created from "--ssh" and
34# virtme-init mistakenly sets identical IPs to the ssh device and additional
35# devices, we instead opt out of using --ssh, add the device manually, and also
36# add the kernel cmdline options that virtme-init uses to setup the interface.
37readonly QEMU_TEST_PORT_FWD="hostfwd=tcp::${TEST_HOST_PORT}-:${TEST_GUEST_PORT}"
38readonly QEMU_SSH_PORT_FWD="hostfwd=tcp::${SSH_HOST_PORT}-:${SSH_GUEST_PORT}"
39readonly KERNEL_CMDLINE="\
40	virtme.dhcp net.ifnames=0 biosdevname=0 \
41	virtme.ssh virtme_ssh_channel=tcp virtme_ssh_user=$USER \
42"
43readonly LOG=$(mktemp /tmp/vsock_vmtest_XXXX.log)
44
45# Namespace tests must use the ns_ prefix. This is checked in check_netns() and
46# is used to determine if a test needs namespace setup before test execution.
47readonly TEST_NAMES=(
48	vm_server_host_client
49	vm_client_host_server
50	vm_loopback
51	ns_host_vsock_ns_mode_ok
52	ns_host_vsock_child_ns_mode_ok
53)
54readonly TEST_DESCS=(
55	# vm_server_host_client
56	"Run vsock_test in server mode on the VM and in client mode on the host."
57
58	# vm_client_host_server
59	"Run vsock_test in client mode on the VM and in server mode on the host."
60
61	# vm_loopback
62	"Run vsock_test using the loopback transport in the VM."
63
64	# ns_host_vsock_ns_mode_ok
65	"Check /proc/sys/net/vsock/ns_mode strings on the host."
66
67	# ns_host_vsock_child_ns_mode_ok
68	"Check /proc/sys/net/vsock/ns_mode is read-only and child_ns_mode is writable."
69)
70
71readonly USE_SHARED_VM=(
72	vm_server_host_client
73	vm_client_host_server
74	vm_loopback
75)
76readonly NS_MODES=("local" "global")
77
78VERBOSE=0
79
80usage() {
81	local name
82	local desc
83	local i
84
85	echo
86	echo "$0 [OPTIONS] [TEST]..."
87	echo "If no TEST argument is given, all tests will be run."
88	echo
89	echo "Options"
90	echo "  -b: build the kernel from the current source tree and use it for guest VMs"
91	echo "  -q: set the path to or name of qemu binary"
92	echo "  -v: verbose output"
93	echo
94	echo "Available tests"
95
96	for ((i = 0; i < ${#TEST_NAMES[@]}; i++)); do
97		name=${TEST_NAMES[${i}]}
98		desc=${TEST_DESCS[${i}]}
99		printf "\t%-35s%-35s\n" "${name}" "${desc}"
100	done
101	echo
102
103	exit 1
104}
105
106die() {
107	echo "$*" >&2
108	exit "${KSFT_FAIL}"
109}
110
111check_result() {
112	local rc arg
113
114	rc=$1
115	arg=$2
116
117	cnt_total=$(( cnt_total + 1 ))
118
119	if [[ ${rc} -eq ${KSFT_PASS} ]]; then
120		cnt_pass=$(( cnt_pass + 1 ))
121		echo "ok ${cnt_total} ${arg}"
122	elif [[ ${rc} -eq ${KSFT_SKIP} ]]; then
123		cnt_skip=$(( cnt_skip + 1 ))
124		echo "ok ${cnt_total} ${arg} # SKIP"
125	elif [[ ${rc} -eq ${KSFT_FAIL} ]]; then
126		cnt_fail=$(( cnt_fail + 1 ))
127		echo "not ok ${cnt_total} ${arg} # exit=${rc}"
128	fi
129}
130
131add_namespaces() {
132	local orig_mode
133	orig_mode=$(cat /proc/sys/net/vsock/child_ns_mode)
134
135	for mode in "${NS_MODES[@]}"; do
136		echo "${mode}" > /proc/sys/net/vsock/child_ns_mode
137		ip netns add "${mode}0" 2>/dev/null
138		ip netns add "${mode}1" 2>/dev/null
139	done
140
141	echo "${orig_mode}" > /proc/sys/net/vsock/child_ns_mode
142}
143
144init_namespaces() {
145	for mode in "${NS_MODES[@]}"; do
146		# we need lo for qemu port forwarding
147		ip netns exec "${mode}0" ip link set dev lo up
148		ip netns exec "${mode}1" ip link set dev lo up
149	done
150}
151
152del_namespaces() {
153	for mode in "${NS_MODES[@]}"; do
154		ip netns del "${mode}0" &>/dev/null
155		ip netns del "${mode}1" &>/dev/null
156		log_host "removed ns ${mode}0"
157		log_host "removed ns ${mode}1"
158	done
159}
160
161vm_ssh() {
162	local ns_exec
163
164	if [[ "${1}" == init_ns ]]; then
165		ns_exec=""
166	else
167		ns_exec="ip netns exec ${1}"
168	fi
169
170	shift
171
172	${ns_exec} ssh -q -o UserKnownHostsFile=/dev/null -p "${SSH_HOST_PORT}" localhost "$@"
173
174	return $?
175}
176
177cleanup() {
178	terminate_pidfiles "${!PIDFILES[@]}"
179	del_namespaces
180}
181
182check_args() {
183	local found
184
185	for arg in "$@"; do
186		found=0
187		for name in "${TEST_NAMES[@]}"; do
188			if [[ "${name}" = "${arg}" ]]; then
189				found=1
190				break
191			fi
192		done
193
194		if [[ "${found}" -eq 0 ]]; then
195			echo "${arg} is not an available test" >&2
196			usage
197		fi
198	done
199
200	for arg in "$@"; do
201		if ! command -v > /dev/null "test_${arg}"; then
202			echo "Test ${arg} not found" >&2
203			usage
204		fi
205	done
206}
207
208check_deps() {
209	for dep in vng ${QEMU} busybox pkill ssh ss; do
210		if [[ ! -x $(command -v "${dep}") ]]; then
211			echo -e "skip:    dependency ${dep} not found!\n"
212			exit "${KSFT_SKIP}"
213		fi
214	done
215
216	if [[ ! -x $(command -v "${VSOCK_TEST}") ]]; then
217		printf "skip:    %s not found!" "${VSOCK_TEST}"
218		printf " Please build the kselftest vsock target.\n"
219		exit "${KSFT_SKIP}"
220	fi
221}
222
223check_netns() {
224	local tname=$1
225
226	# If the test requires NS support, check if NS support exists
227	# using /proc/self/ns
228	if [[ "${tname}" =~ ^ns_ ]] &&
229	   [[ ! -e /proc/self/ns ]]; then
230		log_host "No NS support detected for test ${tname}"
231		return 1
232	fi
233
234	return 0
235}
236
237check_vng() {
238	local tested_versions
239	local version
240	local ok
241
242	tested_versions=("1.33" "1.36" "1.37")
243	version="$(vng --version)"
244
245	ok=0
246	for tv in "${tested_versions[@]}"; do
247		if [[ "${version}" == *"${tv}"* ]]; then
248			ok=1
249			break
250		fi
251	done
252
253	if [[ ! "${ok}" -eq 1 ]]; then
254		printf "warning: vng version '%s' has not been tested and may " "${version}" >&2
255		printf "not function properly.\n\tThe following versions have been tested: " >&2
256		echo "${tested_versions[@]}" >&2
257	fi
258}
259
260handle_build() {
261	if [[ ! "${BUILD}" -eq 1 ]]; then
262		return
263	fi
264
265	if [[ ! -d "${KERNEL_CHECKOUT}" ]]; then
266		echo "-b requires vmtest.sh called from the kernel source tree" >&2
267		exit 1
268	fi
269
270	pushd "${KERNEL_CHECKOUT}" &>/dev/null
271
272	if ! vng --kconfig --config "${SCRIPT_DIR}"/config; then
273		die "failed to generate .config for kernel source tree (${KERNEL_CHECKOUT})"
274	fi
275
276	if ! make -j$(nproc); then
277		die "failed to build kernel from source tree (${KERNEL_CHECKOUT})"
278	fi
279
280	popd &>/dev/null
281}
282
283create_pidfile() {
284	local pidfile
285
286	pidfile=$(mktemp "${PIDFILE_TEMPLATE}")
287	PIDFILES["${pidfile}"]=1
288
289	echo "${pidfile}"
290}
291
292terminate_pidfiles() {
293	local pidfile
294
295	for pidfile in "$@"; do
296		if [[ -s "${pidfile}" ]]; then
297			pkill -SIGTERM -F "${pidfile}" > /dev/null 2>&1
298		fi
299
300		if [[ -e "${pidfile}" ]]; then
301			rm -f "${pidfile}"
302		fi
303
304		unset "PIDFILES[${pidfile}]"
305	done
306}
307
308vm_start() {
309	local pidfile=$1
310	local ns=$2
311	local logfile=/dev/null
312	local verbose_opt=""
313	local kernel_opt=""
314	local qemu_opts=""
315	local ns_exec=""
316	local qemu
317
318	qemu=$(command -v "${QEMU}")
319
320	if [[ "${VERBOSE}" -eq 1 ]]; then
321		verbose_opt="--verbose"
322		logfile=/dev/stdout
323	fi
324
325	qemu_opts="\
326		 -netdev user,id=n0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \
327		 -device virtio-net-pci,netdev=n0 \
328		 -device vhost-vsock-pci,guest-cid=${VSOCK_CID} \
329		--pidfile ${pidfile}
330	"
331
332	if [[ "${BUILD}" -eq 1 ]]; then
333		kernel_opt="${KERNEL_CHECKOUT}"
334	fi
335
336	if [[ "${ns}" != "init_ns" ]]; then
337		ns_exec="ip netns exec ${ns}"
338	fi
339
340	${ns_exec} vng \
341		--run \
342		${kernel_opt} \
343		${verbose_opt} \
344		--qemu-opts="${qemu_opts}" \
345		--qemu="${qemu}" \
346		--user root \
347		--append "${KERNEL_CMDLINE}" \
348		--rw  &> ${logfile} &
349
350	timeout "${WAIT_QEMU}" \
351		bash -c 'while [[ ! -s '"${pidfile}"' ]]; do sleep 1; done; exit 0'
352}
353
354vm_wait_for_ssh() {
355	local ns=$1
356	local i
357
358	i=0
359	while true; do
360		if [[ ${i} -gt ${WAIT_PERIOD_MAX} ]]; then
361			die "Timed out waiting for guest ssh"
362		fi
363
364		if vm_ssh "${ns}" -- true; then
365			break
366		fi
367		i=$(( i + 1 ))
368		sleep ${WAIT_PERIOD}
369	done
370}
371
372# derived from selftests/net/net_helper.sh
373wait_for_listener()
374{
375	local port=$1
376	local interval=$2
377	local max_intervals=$3
378	local protocol=$4
379	local i
380
381	for i in $(seq "${max_intervals}"); do
382		case "${protocol}" in
383		tcp)
384			if ss --listening --tcp --numeric | grep -q ":${port} "; then
385				break
386			fi
387			;;
388		vsock)
389			if ss --listening --vsock --numeric | grep -q ":${port} "; then
390				break
391			fi
392			;;
393		unix)
394			# For unix sockets, port is actually the socket path
395			if ss --listening --unix | grep -q "${port}"; then
396				break
397			fi
398			;;
399		*)
400			echo "Unknown protocol: ${protocol}" >&2
401			break
402			;;
403		esac
404		sleep "${interval}"
405	done
406}
407
408vm_wait_for_listener() {
409	local ns=$1
410	local port=$2
411	local protocol=$3
412
413	vm_ssh "${ns}" <<EOF
414$(declare -f wait_for_listener)
415wait_for_listener ${port} ${WAIT_PERIOD} ${WAIT_PERIOD_MAX} ${protocol}
416EOF
417}
418
419host_wait_for_listener() {
420	local ns=$1
421	local port=$2
422	local protocol=$3
423
424	if [[ "${ns}" == "init_ns" ]]; then
425		wait_for_listener "${port}" "${WAIT_PERIOD}" "${WAIT_PERIOD_MAX}" "${protocol}"
426	else
427		ip netns exec "${ns}" bash <<-EOF
428			$(declare -f wait_for_listener)
429			wait_for_listener ${port} ${WAIT_PERIOD} ${WAIT_PERIOD_MAX} ${protocol}
430		EOF
431	fi
432}
433
434vm_dmesg_oops_count() {
435	local ns=$1
436
437	vm_ssh "${ns}" -- dmesg 2>/dev/null | grep -c -i 'Oops'
438}
439
440vm_dmesg_warn_count() {
441	local ns=$1
442
443	vm_ssh "${ns}" -- dmesg --level=warn 2>/dev/null | grep -c -i 'vsock'
444}
445
446vm_vsock_test() {
447	local ns=$1
448	local host=$2
449	local cid=$3
450	local port=$4
451	local rc
452
453	# log output and use pipefail to respect vsock_test errors
454	set -o pipefail
455	if [[ "${host}" != server ]]; then
456		vm_ssh "${ns}" -- "${VSOCK_TEST}" \
457			--mode=client \
458			--control-host="${host}" \
459			--peer-cid="${cid}" \
460			--control-port="${port}" \
461			2>&1 | log_guest
462		rc=$?
463	else
464		vm_ssh "${ns}" -- "${VSOCK_TEST}" \
465			--mode=server \
466			--peer-cid="${cid}" \
467			--control-port="${port}" \
468			2>&1 | log_guest &
469		rc=$?
470
471		if [[ $rc -ne 0 ]]; then
472			set +o pipefail
473			return $rc
474		fi
475
476		vm_wait_for_listener "${ns}" "${port}" "tcp"
477		rc=$?
478	fi
479	set +o pipefail
480
481	return $rc
482}
483
484host_vsock_test() {
485	local ns=$1
486	local host=$2
487	local cid=$3
488	local port=$4
489	local rc
490
491	local cmd="${VSOCK_TEST}"
492	if [[ "${ns}" != "init_ns" ]]; then
493		cmd="ip netns exec ${ns} ${cmd}"
494	fi
495
496	# log output and use pipefail to respect vsock_test errors
497	set -o pipefail
498	if [[ "${host}" != server ]]; then
499		${cmd} \
500			--mode=client \
501			--peer-cid="${cid}" \
502			--control-host="${host}" \
503			--control-port="${port}" 2>&1 | log_host
504		rc=$?
505	else
506		${cmd} \
507			--mode=server \
508			--peer-cid="${cid}" \
509			--control-port="${port}" 2>&1 | log_host &
510		rc=$?
511
512		if [[ $rc -ne 0 ]]; then
513			set +o pipefail
514			return $rc
515		fi
516
517		host_wait_for_listener "${ns}" "${port}" "tcp"
518		rc=$?
519	fi
520	set +o pipefail
521
522	return $rc
523}
524
525log() {
526	local redirect
527	local prefix
528
529	if [[ ${VERBOSE} -eq 0 ]]; then
530		redirect=/dev/null
531	else
532		redirect=/dev/stdout
533	fi
534
535	prefix="${LOG_PREFIX:-}"
536
537	if [[ "$#" -eq 0 ]]; then
538		if [[ -n "${prefix}" ]]; then
539			awk -v prefix="${prefix}" '{printf "%s: %s\n", prefix, $0}'
540		else
541			cat
542		fi
543	else
544		if [[ -n "${prefix}" ]]; then
545			echo "${prefix}: " "$@"
546		else
547			echo "$@"
548		fi
549	fi | tee -a "${LOG}" > "${redirect}"
550}
551
552log_host() {
553	LOG_PREFIX=host log "$@"
554}
555
556log_guest() {
557	LOG_PREFIX=guest log "$@"
558}
559
560ns_get_mode() {
561	local ns=$1
562
563	ip netns exec "${ns}" cat /proc/sys/net/vsock/ns_mode 2>/dev/null
564}
565
566test_ns_host_vsock_ns_mode_ok() {
567	for mode in "${NS_MODES[@]}"; do
568		local actual
569
570		actual=$(ns_get_mode "${mode}0")
571		if [[ "${actual}" != "${mode}" ]]; then
572			log_host "expected mode ${mode}, got ${actual}"
573			return "${KSFT_FAIL}"
574		fi
575	done
576
577	return "${KSFT_PASS}"
578}
579
580test_ns_host_vsock_child_ns_mode_ok() {
581	local orig_mode
582	local rc
583
584	orig_mode=$(cat /proc/sys/net/vsock/child_ns_mode)
585
586	rc="${KSFT_PASS}"
587	for mode in "${NS_MODES[@]}"; do
588		local ns="${mode}0"
589
590		if echo "${mode}" 2>/dev/null > /proc/sys/net/vsock/ns_mode; then
591			log_host "ns_mode should be read-only but write succeeded"
592			rc="${KSFT_FAIL}"
593			continue
594		fi
595
596		if ! echo "${mode}" > /proc/sys/net/vsock/child_ns_mode; then
597			log_host "child_ns_mode should be writable to ${mode}"
598			rc="${KSFT_FAIL}"
599			continue
600		fi
601	done
602
603	echo "${orig_mode}" > /proc/sys/net/vsock/child_ns_mode
604
605	return "${rc}"
606}
607
608test_vm_server_host_client() {
609	if ! vm_vsock_test "init_ns" "server" 2 "${TEST_GUEST_PORT}"; then
610		return "${KSFT_FAIL}"
611	fi
612
613	if ! host_vsock_test "init_ns" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_PORT}"; then
614		return "${KSFT_FAIL}"
615	fi
616
617	return "${KSFT_PASS}"
618}
619
620test_vm_client_host_server() {
621	if ! host_vsock_test "init_ns" "server" "${VSOCK_CID}" "${TEST_HOST_PORT_LISTENER}"; then
622		return "${KSFT_FAIL}"
623	fi
624
625	if ! vm_vsock_test "init_ns" "10.0.2.2" 2 "${TEST_HOST_PORT_LISTENER}"; then
626		return "${KSFT_FAIL}"
627	fi
628
629	return "${KSFT_PASS}"
630}
631
632test_vm_loopback() {
633	local port=60000 # non-forwarded local port
634
635	vm_ssh "init_ns" -- modprobe vsock_loopback &> /dev/null || :
636
637	if ! vm_vsock_test "init_ns" "server" 1 "${port}"; then
638		return "${KSFT_FAIL}"
639	fi
640
641
642	if ! vm_vsock_test "init_ns" "127.0.0.1" 1 "${port}"; then
643		return "${KSFT_FAIL}"
644	fi
645
646	return "${KSFT_PASS}"
647}
648
649shared_vm_test() {
650	local tname
651
652	tname="${1}"
653
654	for testname in "${USE_SHARED_VM[@]}"; do
655		if [[ "${tname}" == "${testname}" ]]; then
656			return 0
657		fi
658	done
659
660	return 1
661}
662
663shared_vm_tests_requested() {
664	for arg in "$@"; do
665		if shared_vm_test "${arg}"; then
666			return 0
667		fi
668	done
669
670	return 1
671}
672
673run_shared_vm_tests() {
674	local arg
675
676	for arg in "$@"; do
677		if ! shared_vm_test "${arg}"; then
678			continue
679		fi
680
681		if ! check_netns "${arg}"; then
682			check_result "${KSFT_SKIP}" "${arg}"
683			continue
684		fi
685
686		run_shared_vm_test "${arg}"
687		check_result "$?" "${arg}"
688	done
689}
690
691run_shared_vm_test() {
692	local host_oops_cnt_before
693	local host_warn_cnt_before
694	local vm_oops_cnt_before
695	local vm_warn_cnt_before
696	local host_oops_cnt_after
697	local host_warn_cnt_after
698	local vm_oops_cnt_after
699	local vm_warn_cnt_after
700	local name
701	local rc
702
703	host_oops_cnt_before=$(dmesg | grep -c -i 'Oops')
704	host_warn_cnt_before=$(dmesg --level=warn | grep -c -i 'vsock')
705	vm_oops_cnt_before=$(vm_dmesg_oops_count "init_ns")
706	vm_warn_cnt_before=$(vm_dmesg_warn_count "init_ns")
707
708	name=$(echo "${1}" | awk '{ print $1 }')
709	eval test_"${name}"
710	rc=$?
711
712	host_oops_cnt_after=$(dmesg | grep -i 'Oops' | wc -l)
713	if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then
714		echo "FAIL: kernel oops detected on host" | log_host
715		rc=$KSFT_FAIL
716	fi
717
718	host_warn_cnt_after=$(dmesg --level=warn | grep -c -i 'vsock')
719	if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then
720		echo "FAIL: kernel warning detected on host" | log_host
721		rc=$KSFT_FAIL
722	fi
723
724	vm_oops_cnt_after=$(vm_dmesg_oops_count "init_ns")
725	if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then
726		echo "FAIL: kernel oops detected on vm" | log_host
727		rc=$KSFT_FAIL
728	fi
729
730	vm_warn_cnt_after=$(vm_dmesg_warn_count "init_ns")
731	if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then
732		echo "FAIL: kernel warning detected on vm" | log_host
733		rc=$KSFT_FAIL
734	fi
735
736	return "${rc}"
737}
738
739run_ns_tests() {
740	for arg in "${ARGS[@]}"; do
741		if shared_vm_test "${arg}"; then
742			continue
743		fi
744
745		if ! check_netns "${arg}"; then
746			check_result "${KSFT_SKIP}" "${arg}"
747			continue
748		fi
749
750		add_namespaces
751
752		name=$(echo "${arg}" | awk '{ print $1 }')
753		log_host "Executing test_${name}"
754
755		host_oops_before=$(dmesg 2>/dev/null | grep -c -i 'Oops')
756		host_warn_before=$(dmesg --level=warn 2>/dev/null | grep -c -i 'vsock')
757		eval test_"${name}"
758		rc=$?
759
760		host_oops_after=$(dmesg 2>/dev/null | grep -c -i 'Oops')
761		if [[ "${host_oops_after}" -gt "${host_oops_before}" ]]; then
762			echo "FAIL: kernel oops detected on host" | log_host
763			check_result "${KSFT_FAIL}" "${name}"
764			del_namespaces
765			continue
766		fi
767
768		host_warn_after=$(dmesg --level=warn 2>/dev/null | grep -c -i 'vsock')
769		if [[ "${host_warn_after}" -gt "${host_warn_before}" ]]; then
770			echo "FAIL: kernel warning detected on host" | log_host
771			check_result "${KSFT_FAIL}" "${name}"
772			del_namespaces
773			continue
774		fi
775
776		check_result "${rc}" "${name}"
777
778		del_namespaces
779	done
780}
781
782BUILD=0
783QEMU="qemu-system-$(uname -m)"
784
785while getopts :hvsq:b o
786do
787	case $o in
788	v) VERBOSE=1;;
789	b) BUILD=1;;
790	q) QEMU=$OPTARG;;
791	h|*) usage;;
792	esac
793done
794shift $((OPTIND-1))
795
796trap cleanup EXIT
797
798if [[ ${#} -eq 0 ]]; then
799	ARGS=("${TEST_NAMES[@]}")
800else
801	ARGS=("$@")
802fi
803
804check_args "${ARGS[@]}"
805check_deps
806check_vng
807handle_build
808
809echo "1..${#ARGS[@]}"
810
811cnt_pass=0
812cnt_fail=0
813cnt_skip=0
814cnt_total=0
815
816if shared_vm_tests_requested "${ARGS[@]}"; then
817	log_host "Booting up VM"
818	pidfile="$(create_pidfile)"
819	vm_start "${pidfile}" "init_ns"
820	vm_wait_for_ssh "init_ns"
821	log_host "VM booted up"
822
823	run_shared_vm_tests "${ARGS[@]}"
824	terminate_pidfiles "${pidfile}"
825fi
826
827run_ns_tests "${ARGS[@]}"
828
829echo "SUMMARY: PASS=${cnt_pass} SKIP=${cnt_skip} FAIL=${cnt_fail}"
830echo "Log: ${LOG}"
831
832if [ $((cnt_pass + cnt_skip)) -eq ${cnt_total} ]; then
833	exit "$KSFT_PASS"
834else
835	exit "$KSFT_FAIL"
836fi
837