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 1614