projects/linux·blob master · refs

1613 lines · 35.7 KiB

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