From nobody Thu Oct 2 13:01:37 2025 Received: from mail-pj1-f42.google.com (mail-pj1-f42.google.com [209.85.216.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 874DE2FA0DB for ; Tue, 16 Sep 2025 23:44:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758066250; cv=none; b=JiKHnDrpCwTTLwjlMsRTeY7cpogsV4WR/yiI6ZHz4m9/nttLU74AE2HG+8xZ0ymG7f+3XvvyEIytf/GPSMmkQIAsBunSz9qc6r3qRcRF+eyL++SXzVfLhWWAfAWRstmiePmOQvv9eJO739mMtYT2izZvgDrd0Nm8yavSYwdWWYU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758066250; c=relaxed/simple; bh=9m4cWG4BpkgT72TcRgDZ7iYYFF7UIJ8xpfr38T+6gzU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GmYHl82clalkZe64f+d4z+HCALhXKMn8wwEs4i1O3xDY6RneBPxccqrzhCp6/B7DKHTtxHtPWV7GfzV1ADYqZg3a66c2rBp/12o67XdhedV6fsp3rZozR+F5e5e9NaGMhqMevmAiEHr6BGRfx0SP/fggl9B4PPzh+FlvPn69ykc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=O3JuU30k; arc=none smtp.client-ip=209.85.216.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="O3JuU30k" Received: by mail-pj1-f42.google.com with SMTP id 98e67ed59e1d1-32e83953989so2295573a91.1 for ; Tue, 16 Sep 2025 16:44:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758066246; x=1758671046; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=Td1ZAyn4msZcwjT2il97ffZ1NfsG6+V/WWfXT5WF56k=; b=O3JuU30ke2+jYMbIoeNkRjflg+2bftuFjSCVYaFilXEFng0X29ituxT3nP/eX9HubJ Y8yovVW91AUY+dKY2qyufCuns8uPDqJYCXhfX/gCm4G8OtXR/Xq20bfZ62raXgx//y5a 8im1WSsTwo3Bp6uprbc3doEakVySJtSIuu88U/B8zhk5yrlYFG6PzMHHLLuRNkb18GXF 1UfPrJEOTZUUzTEXs9RnkZrp5VLtgWZqIrE0g0cuJiQUo8hcd9mF8+M3W6mvh2/tCBk4 XCHsf+cUIv8qck6NWvJ5yXo8t5IHhIQ5/4lJiKM5x09ca+mgja91bhCaaodDFhO+x0Pf OwTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758066246; x=1758671046; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Td1ZAyn4msZcwjT2il97ffZ1NfsG6+V/WWfXT5WF56k=; b=hG0wYXFOL2M8h9UqrxxpyQo9zGVGWZO0cBin9D73xta7M4ug8goig0gQq7PEeZkgQC 7POvu5syGOjx6SXzhVf3XKbSN6hcUXCmKheO7WMjfrWTHxgbPAZxhWovo3CBgcoausxX CpxtO3MYcBwJWYMUtB8WVh8WJqpYekRDlMnY0zPR/YfKIe/V8vihJo0qM09pYLz/+pOq 49SvHW6cQqCJ6eeabf50AhLorGTpzyFSHK1e3XSE92IvNi5rb8Ecsa+qjwsOvBi4XBlS HAQOyFAMduHX76qADdFK6/O/kfIeiKQxg0Rl3W6e5tVSo+P3hJow7Ws+3YcZKTquJzZE twZw== X-Forwarded-Encrypted: i=1; AJvYcCWZZ5342jBzSmz+l6s23iK0ZJ1OgBLCz1Xp2x65AZo94Dp+xI+BPoD/PZj9H2+9rtbzIg25At2XMBoqt3M=@vger.kernel.org X-Gm-Message-State: AOJu0Yxg/n8wRnkI5eZgIY3SWyl1WIFhYmhwFwQgk4WVE9bEXB/Dx+9b ELqSN7tM5W2UZ8ttt+ZwZ1GqCBP7h5JdJhN6lRQoQKv09E7x08aLyBr/ X-Gm-Gg: ASbGncsK9jcZLFpLjP/jwj/yWMcsrBvIvBWraslx9zqbivPa+lIBdMIqzIKxzOzZs3E xnnK09JQ95fQgSLgty8FgMN/f/7ugVzFCS6ul8cW7fsf0XMTH4+RKPk2ty05jgxKK0onfuuMY6P wnzyP8pqhHBqtNG5Z5jYoeqGCXPCmcW9pSEKqWmkowyQ5M33gheRfk+AajGnSXf77EXkRB+nkDD qP2TKAlD/UlvLVlLhfg92N/3YmqTwf4Qt8Ss/ijwZizxo7d0WBqLcxLWhRP/lwFKnxQ3C5HvkFk lsgiKE7asqBR52TZV9REsHwbdtMA1fl4Phjq4SXvAKG1wVE7VjHul/vtI78vME3K6XDjA9LOodV tFQssLMK0BDFcRFdAGG7jtzS/PPcAIw== X-Google-Smtp-Source: AGHT+IGxopyi0LrKmIsjPR0jM0XuFbdXHCSEPfpcqv1nkQcZnI4hlSriBi029oyZ0ZKsW9nZNb+WxQ== X-Received: by 2002:a17:90b:2d86:b0:329:e729:b2a1 with SMTP id 98e67ed59e1d1-32ee3f8cc13mr173457a91.35.1758066245594; Tue, 16 Sep 2025 16:44:05 -0700 (PDT) Received: from localhost ([2a03:2880:2ff:1::]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b54de4c1c41sm3012422a12.24.2025.09.16.16.44.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 16 Sep 2025 16:44:04 -0700 (PDT) From: Bobby Eshleman Date: Tue, 16 Sep 2025 16:43:53 -0700 Subject: [PATCH net-next v6 9/9] selftests/vsock: add namespace tests Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250916-vsock-vmtest-v6-9-064d2eb0c89d@meta.com> References: <20250916-vsock-vmtest-v6-0-064d2eb0c89d@meta.com> In-Reply-To: <20250916-vsock-vmtest-v6-0-064d2eb0c89d@meta.com> To: Stefano Garzarella , Shuah Khan , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Stefan Hajnoczi , "Michael S. Tsirkin" , Jason Wang , Xuan Zhuo , =?utf-8?q?Eugenio_P=C3=A9rez?= , "K. Y. Srinivasan" , Haiyang Zhang , Wei Liu , Dexuan Cui , Bryan Tan , Vishnu Dasa , Broadcom internal kernel review list Cc: virtualization@lists.linux.dev, netdev@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, kvm@vger.kernel.org, linux-hyperv@vger.kernel.org, Bobby Eshleman , berrange@redhat.com, Bobby Eshleman X-Mailer: b4 0.13.0 From: Bobby Eshleman Add tests for namespace support in vsock. Use socat for basic connection failure tests and vsock_test for full functionality tests when communication is expected to succeed. vsock_test is not used for failure cases because in theory vsock_test could allow connection and some traffic flow but fail on some other case (e.g., fail on MSG_ZEROCOPY). Tests cover all cases of clients and servers being in all variants of local ns, global ns, host process, and VM process. Legacy tests are retained and executed in the init ns. Signed-off-by: Bobby Eshleman --- Changes in v6: - check for namespace support in vmtest.sh Changes in v5: - use /proc/sys/net/vsock/ns_mode - clarify logic of tests that reuse the same VM and tests that require netns setup - fix unassigned BUILD bug --- tools/testing/selftests/vsock/vmtest.sh | 954 ++++++++++++++++++++++++++++= ---- 1 file changed, 849 insertions(+), 105 deletions(-) diff --git a/tools/testing/selftests/vsock/vmtest.sh b/tools/testing/selfte= sts/vsock/vmtest.sh index 5e36d1068f6f..59621b32cf1a 100755 --- a/tools/testing/selftests/vsock/vmtest.sh +++ b/tools/testing/selftests/vsock/vmtest.sh @@ -7,6 +7,7 @@ # * virtme-ng # * busybox-static (used by virtme-ng) # * qemu (used by virtme-ng) +# * socat =20 readonly SCRIPT_DIR=3D"$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && p= wd -P)" readonly KERNEL_CHECKOUT=3D$(realpath "${SCRIPT_DIR}"/../../../../) @@ -23,7 +24,7 @@ readonly VSOCK_CID=3D1234 readonly WAIT_PERIOD=3D3 readonly WAIT_PERIOD_MAX=3D60 readonly WAIT_TOTAL=3D$(( WAIT_PERIOD * WAIT_PERIOD_MAX )) -readonly QEMU_PIDFILE=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) +readonly WAIT_QEMU=3D5 =20 # virtme-ng offers a netdev for ssh when using "--ssh", but we also need a # control port forwarded for vsock_test. Because virtme-ng doesn't support @@ -33,23 +34,146 @@ readonly QEMU_PIDFILE=3D$(mktemp /tmp/qemu_vsock_vmtes= t_XXXX.pid) # add the kernel cmdline options that virtme-init uses to setup the interf= ace. readonly QEMU_TEST_PORT_FWD=3D"hostfwd=3Dtcp::${TEST_HOST_PORT}-:${TEST_GU= EST_PORT}" readonly QEMU_SSH_PORT_FWD=3D"hostfwd=3Dtcp::${SSH_HOST_PORT}-:${SSH_GUEST= _PORT}" -readonly QEMU_OPTS=3D"\ - -netdev user,id=3Dn0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \ - -device virtio-net-pci,netdev=3Dn0 \ - -device vhost-vsock-pci,guest-cid=3D${VSOCK_CID} \ - --pidfile ${QEMU_PIDFILE} \ -" readonly KERNEL_CMDLINE=3D"\ virtme.dhcp net.ifnames=3D0 biosdevname=3D0 \ virtme.ssh virtme_ssh_channel=3Dtcp virtme_ssh_user=3D$USER \ " readonly LOG=3D$(mktemp /tmp/vsock_vmtest_XXXX.log) -readonly TEST_NAMES=3D(vm_server_host_client vm_client_host_server vm_loop= back) +readonly TEST_NAMES=3D( + vm_server_host_client + vm_client_host_server + vm_loopback + host_vsock_ns_mode_ok + host_vsock_ns_mode_write_once_ok + global_same_cid_fails + local_same_cid_ok + global_local_same_cid_ok + local_global_same_cid_ok + diff_ns_global_host_connect_to_global_vm_ok + diff_ns_global_host_connect_to_local_vm_fails + diff_ns_global_vm_connect_to_global_host_ok + diff_ns_global_vm_connect_to_local_host_fails + diff_ns_local_host_connect_to_local_vm_fails + diff_ns_local_vm_connect_to_local_host_fails + diff_ns_global_to_local_loopback_local_fails + diff_ns_local_to_global_loopback_fails + diff_ns_local_to_local_loopback_fails + diff_ns_global_to_global_loopback_ok + same_ns_local_loopback_ok + same_ns_local_host_connect_to_local_vm_ok + same_ns_local_vm_connect_to_local_host_ok +) + readonly TEST_DESCS=3D( + # vm_server_host_client "Run vsock_test in server mode on the VM and in client mode on the host." + + # vm_client_host_server "Run vsock_test in client mode on the VM and in server mode on the host." + + # vm_loopback "Run vsock_test using the loopback transport in the VM." + + # host_vsock_ns_mode_ok + "Check /proc/sys/net/vsock/ns_mode strings on the host." + + # host_vsock_ns_mode_write_once_ok + "Check /proc/sys/net/vsock/ns_mode is write-once on the host." + + # global_same_cid_fails + "Check QEMU fails to start two VMs with same CID in two different global = namespaces." + + # local_same_cid_ok + "Check QEMU successfully starts two VMs with same CID in two different lo= cal namespaces." + + # global_local_same_cid_ok + "Check QEMU successfully starts one VM in a global ns and then another VM= in a local ns with the same CID." + + # local_global_same_cid_ok + "Check QEMU successfully starts one VM in a local ns and then another VM = in a global ns with the same CID." + + # diff_ns_global_host_connect_to_global_vm_ok + "Run vsock_test client in global ns with server in VM in another global n= s." + + # diff_ns_global_host_connect_to_local_vm_fails + "Run socat to test a process in a global ns fails to connect to a VM in a= local ns." + + # diff_ns_global_vm_connect_to_global_host_ok + "Run vsock_test client in VM in a global ns with server in another global= ns." + + # diff_ns_global_vm_connect_to_local_host_fails + "Run socat to test a VM in a global ns fails to connect to a host process= in a local ns." + + # diff_ns_local_host_connect_to_local_vm_fails + "Run socat to test a host process in a local ns fails to connect to a VM = in another local ns." + + # diff_ns_local_vm_connect_to_local_host_fails + "Run socat to test a VM in a local ns fails to connect to a host process = in another local ns." + + # diff_ns_global_to_local_loopback_local_fails + "Run socat to test a loopback vsock in a global ns fails to connect to a = vsock in a local ns." + + # diff_ns_local_to_global_loopback_fails + "Run socat to test a loopback vsock in a local ns fails to connect to a v= sock in a global ns." + + # diff_ns_local_to_local_loopback_fails + "Run socat to test a loopback vsock in a local ns fails to connect to a v= sock in another local ns." + + # diff_ns_global_to_global_loopback_ok + "Run socat to test a loopback vsock in a global ns successfully connects = to a vsock in another global ns." + + # same_ns_local_loopback_ok + "Run socat to test a loopback vsock in a local ns successfully connects t= o a vsock in the same ns." + + # same_ns_local_host_connect_to_local_vm_ok + "Run vsock_test client in a local ns with server in VM in same ns." + + # same_ns_local_vm_connect_to_local_host_ok + "Run vsock_test client in VM in a local ns with server in same ns." +) + +readonly USE_SHARED_VM=3D(vm_server_host_client vm_client_host_server vm_l= oopback) +readonly USE_INIT_NETNS=3D( + global_same_cid_fails + local_same_cid_ok + global_local_same_cid_ok + local_global_same_cid_ok + diff_ns_global_host_connect_to_global_vm_ok + diff_ns_global_host_connect_to_local_vm_fails + diff_ns_global_vm_connect_to_global_host_ok + diff_ns_global_vm_connect_to_local_host_fails + diff_ns_local_host_connect_to_local_vm_fails + diff_ns_local_vm_connect_to_local_host_fails + diff_ns_global_to_local_loopback_local_fails + diff_ns_local_to_global_loopback_fails + diff_ns_local_to_local_loopback_fails + diff_ns_global_to_global_loopback_ok + same_ns_local_loopback_ok + same_ns_local_host_connect_to_local_vm_ok + same_ns_local_vm_connect_to_local_host_ok +) +readonly REQUIRES_NETNS=3D( + host_vsock_ns_mode_ok + host_vsock_ns_mode_write_once_ok + global_same_cid_fails + local_same_cid_ok + global_local_same_cid_ok + local_global_same_cid_ok + diff_ns_global_host_connect_to_global_vm_ok + diff_ns_global_host_connect_to_local_vm_fails + diff_ns_global_vm_connect_to_global_host_ok + diff_ns_global_vm_connect_to_local_host_fails + diff_ns_local_host_connect_to_local_vm_fails + diff_ns_local_vm_connect_to_local_host_fails + diff_ns_global_to_local_loopback_local_fails + diff_ns_local_to_global_loopback_fails + diff_ns_local_to_local_loopback_fails + diff_ns_global_to_global_loopback_ok + same_ns_local_loopback_ok + same_ns_local_host_connect_to_local_vm_ok + same_ns_local_vm_connect_to_local_host_ok ) +readonly MODES=3D("local" "global") =20 readonly LOG_LEVEL_DEBUG=3D0 readonly LOG_LEVEL_INFO=3D1 @@ -58,6 +182,12 @@ readonly LOG_LEVEL_ERROR=3D3 =20 VERBOSE=3D"${LOG_LEVEL_WARN}" =20 +# Test pass/fail counters +cnt_pass=3D0 +cnt_fail=3D0 +cnt_skip=3D0 +cnt_total=3D0 + usage() { local name local desc @@ -77,7 +207,7 @@ usage() { for ((i =3D 0; i < ${#TEST_NAMES[@]}; i++)); do name=3D${TEST_NAMES[${i}]} desc=3D${TEST_DESCS[${i}]} - printf "\t%-35s%-35s\n" "${name}" "${desc}" + printf "\t%-55s%-35s\n" "${name}" "${desc}" done echo =20 @@ -89,21 +219,87 @@ die() { exit "${KSFT_FAIL}" } =20 +add_namespaces() { + # add namespaces local0, local1, global0, and global1 + for mode in "${MODES[@]}"; do + ip netns add "${mode}0" 2>/dev/null + ip netns add "${mode}1" 2>/dev/null + done +} + +init_namespaces() { + for mode in "${MODES[@]}"; do + ns_set_mode "${mode}0" "${mode}" + ns_set_mode "${mode}1" "${mode}" + + log_host "set ns ${mode}0 to mode ${mode}" + log_host "set ns ${mode}1 to mode ${mode}" + + # we need lo for qemu port forwarding + ip netns exec "${mode}0" ip link set dev lo up + ip netns exec "${mode}1" ip link set dev lo up + done +} + +del_namespaces() { + for mode in "${MODES[@]}"; do + ip netns del "${mode}0" + ip netns del "${mode}1" + log_host "removed ns ${mode}0" + log_host "removed ns ${mode}1" + done &>/dev/null +} + +ns_set_mode() { + local ns=3D$1 + local mode=3D$2 + + echo "${mode}" | ip netns exec "${ns}" \ + tee /proc/sys/net/vsock/ns_mode &>/dev/null +} + vm_ssh() { - ssh -q -o UserKnownHostsFile=3D/dev/null -p ${SSH_HOST_PORT} localhost "$= @" + local ns_exec + + if [[ "${1}" =3D=3D none ]]; then + local ns_exec=3D"" + else + local ns_exec=3D"ip netns exec ${1}" + fi + + shift + + ${ns_exec} ssh -q -o UserKnownHostsFile=3D/dev/null -p ${SSH_HOST_PORT} l= ocalhost $* + return $? } =20 cleanup() { - if [[ -s "${QEMU_PIDFILE}" ]]; then - pkill -SIGTERM -F "${QEMU_PIDFILE}" > /dev/null 2>&1 - fi + del_namespaces +} =20 - # If failure occurred during or before qemu start up, then we need - # to clean this up ourselves. - if [[ -e "${QEMU_PIDFILE}" ]]; then - rm "${QEMU_PIDFILE}" - fi +terminate_pidfiles() { + local pidfile + + for pidfile in "$@"; do + if [[ -s "${pidfile}" ]]; then + pkill -SIGTERM -F "${pidfile}" 2>&1 > /dev/null + fi + + # If failure occurred during or before qemu start up, then we need + # to clean this up ourselves. + if [[ -e "${pidfile}" ]]; then + rm -f "${pidfile}" + fi + done +} + +terminate_pids() { + local pid + + for pid in "$@"; do + kill -SIGTERM "${pid}" &>/dev/null || : + done } =20 check_args() { @@ -133,7 +329,7 @@ check_args() { } =20 check_deps() { - for dep in vng ${QEMU} busybox pkill ssh; do + for dep in vng ${QEMU} busybox pkill ssh socat; do if [[ ! -x $(command -v "${dep}") ]]; then echo -e "skip: dependency ${dep} not found!\n" exit "${KSFT_SKIP}" @@ -147,6 +343,20 @@ check_deps() { fi } =20 +check_test_deps() { + local tname=3D$1 + + # If the test requires NS support, check if NS support exists + # using /proc/self/ns + if [[ "${tname}" =3D~ "${REQUIRES_NETNS[@]}" ]] && + [[ ! -e /proc/self/ns ]]; then + log_host "No NS support detected for test ${tname}" + return 1 + fi + + return 0 +} + check_vng() { local tested_versions local version @@ -170,6 +380,20 @@ check_vng() { fi } =20 +check_socat() { + local support_string + + support_string=3D"$(socat -V)" + + if [[ "${support_string}" !=3D *"WITH_VSOCK 1"* ]]; then + die "err: socat is missing vsock support" + fi + + if [[ "${support_string}" !=3D *"WITH_UNIX 1"* ]]; then + die "err: socat is missing unix support" + fi +} + handle_build() { if [[ ! "${BUILD}" -eq 1 ]]; then return @@ -194,9 +418,14 @@ handle_build() { } =20 vm_start() { + local cid=3D$1 + local ns=3D$2 + local pidfile=3D$3 local logfile=3D/dev/null local verbose_opt=3D"" + local qemu_opts=3D"" local kernel_opt=3D"" + local ns_exec=3D"" local qemu =20 qemu=3D$(command -v "${QEMU}") @@ -206,27 +435,37 @@ vm_start() { logfile=3D/dev/stdout fi =20 + qemu_opts=3D"\ + -netdev user,id=3Dn0,${QEMU_TEST_PORT_FWD},${QEMU_SSH_PORT_FWD} \ + -device virtio-net-pci,netdev=3Dn0 \ + ${QEMU_OPTS} -device vhost-vsock-pci,guest-cid=3D${cid} \ + --pidfile ${pidfile} + " + if [[ "${BUILD}" -eq 1 ]]; then kernel_opt=3D"${KERNEL_CHECKOUT}" fi =20 - vng \ + if [[ "${ns}" !=3D "none" ]]; then + ns_exec=3D"ip netns exec ${ns}" + fi + + ${ns_exec} vng \ --run \ ${kernel_opt} \ ${verbose_opt} \ - --qemu-opts=3D"${QEMU_OPTS}" \ + --qemu-opts=3D"${qemu_opts}" \ --qemu=3D"${qemu}" \ --user root \ --append "${KERNEL_CMDLINE}" \ --rw &> ${logfile} & =20 - if ! timeout ${WAIT_TOTAL} \ - bash -c 'while [[ ! -s '"${QEMU_PIDFILE}"' ]]; do sleep 1; done; exit 0'= ; then - die "failed to boot VM" - fi + timeout "${WAIT_QEMU}" \ + bash -c 'while [[ ! -s '"${pidfile}"' ]]; do sleep 1; done; exit 0' } =20 vm_wait_for_ssh() { + local ns=3D$1 local i =20 i=3D0 @@ -234,7 +473,8 @@ vm_wait_for_ssh() { if [[ ${i} -gt ${WAIT_PERIOD_MAX} ]]; then die "Timed out waiting for guest ssh" fi - if vm_ssh -- true; then + + if vm_ssh "${ns}" -- true; then break fi i=3D$(( i + 1 )) @@ -269,6 +509,7 @@ wait_for_listener() grep -q "${pattern}"; then break fi + sleep "${interval}" done =20 @@ -278,17 +519,29 @@ wait_for_listener() } =20 vm_wait_for_listener() { - local port=3D$1 + local ns=3D$1 + local port=3D$2 + + log "Waiting for listener on port ${port} on vm" =20 - vm_ssh <&1 | log_host & + for mode in "${MODES[@]}"; do + local ns=3D"${mode}0" + if ! ns_set_mode "${ns}" "${mode}"; then + del_namespaces + return "${KSFT_FAIL}" + fi =20 - host_wait_for_listener + # try writing again and expect failure + if ns_set_mode "${ns}" "${mode}"; then + del_namespaces + return "${KSFT_FAIL}" + fi + done =20 - vm_ssh -- "${VSOCK_TEST}" \ - --mode=3Dclient \ - --control-host=3D10.0.2.2 \ - --peer-cid=3D2 \ - --control-port=3D"${TEST_HOST_PORT_LISTENER}" 2>&1 | log_guest + del_namespaces =20 - return $? + return "${KSFT_PASS}" } =20 -test_vm_loopback() { - local port=3D60000 # non-forwarded local port +namespaces_can_boot_same_cid() { + local ns0=3D$1 + local ns1=3D$2 + local pidfile1 pidfile2 + local cid=3D20 + readonly cid + local rc =20 - vm_ssh -- "${VSOCK_TEST}" \ - --mode=3Dserver \ - --control-port=3D"${port}" \ - --peer-cid=3D1 2>&1 | log_guest & + pidfile1=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + vm_start "${cid}" "${ns0}" "${pidfile1}" =20 - vm_wait_for_listener "${port}" + pidfile2=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + vm_start "${cid}" "${ns1}" "${pidfile2}" =20 - vm_ssh -- "${VSOCK_TEST}" \ - --mode=3Dclient \ - --control-host=3D"127.0.0.1" \ - --control-port=3D"${port}" \ - --peer-cid=3D1 2>&1 | log_guest + rc=3D$? + terminate_pidfiles "${pidfile1}" "${pidfile2}" =20 - return $? + return $rc +} + +test_global_same_cid_fails() { + if namespaces_can_boot_same_cid "global0" "global1"; then + return "${KSFT_FAIL}" + fi + + return "${KSFT_PASS}" +} + +test_local_global_same_cid_ok() { + if namespaces_can_boot_same_cid "local0" "global0"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_global_local_same_cid_ok() { + if namespaces_can_boot_same_cid "global0" "local0"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_local_same_cid_ok() { + if namespaces_can_boot_same_cid "local0" "local0"; then + return "${KSFT_FAIL}" + fi + + return "${KSFT_PASS}" +} + +test_diff_ns_global_host_connect_to_global_vm_ok() { + local pids pid pidfile + local ns0 ns1 port + declare -a pids + local unixfile + ns0=3D"global0" + ns1=3D"global1" + port=3D1234 + local rc + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + + if ! vm_start "${VSOCK_CID}" "${ns0}" "${pidfile}"; then + return "${KSFT_FAIL}" + fi + + unixfile=3D$(mktemp -u /tmp/XXXX.sock) + ip netns exec "${ns1}" \ + socat TCP-LISTEN:"${TEST_HOST_PORT}",fork \ + UNIX-CONNECT:"${unixfile}" & + pids+=3D($!) + host_wait_for_listener "${ns1}" "${TEST_HOST_PORT}" + + ip netns exec "${ns0}" socat UNIX-LISTEN:"${unixfile}",fork \ + TCP-CONNECT:localhost:"${TEST_HOST_PORT}" & + pids+=3D($!) + + vm_vsock_test "${ns0}" "server" 2 "${TEST_GUEST_PORT}" + vm_wait_for_listener "${ns0}" "${TEST_GUEST_PORT}" + host_vsock_test "${ns1}" "client" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST= _PORT}" + rc=3D$? + + for pid in "${pids[@]}"; do + if [[ "$(jobs -p)" =3D *"${pid}"* ]]; then + kill -SIGTERM "${pid}" &>/dev/null + fi + done + + terminate_pidfiles "${pidfile}" + + if [[ $rc -ne 0 ]]; then + return "${KSFT_FAIL}" + fi + + return "${KSFT_PASS}" +} + +test_diff_ns_global_host_connect_to_local_vm_fails() { + local ns0=3D"global0" + local ns1=3D"local0" + local port=3D12345 + local pidfile + local result + local pid + + outfile=3D$(mktemp) + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + if ! vm_start "${VSOCK_CID}" "${ns1}" "${pidfile}"; then + log_host "failed to start vm (cid=3D${VSOCK_CID}, ns=3D${ns0})" + return $KSFT_FAIL + fi + + vm_wait_for_ssh "${ns1}" + vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" & + echo TEST | ip netns exec "${ns0}" \ + socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null + + terminate_pidfiles "${pidfile}" + + result=3D$(cat "${outfile}") + rm -f "${outfile}" + + if [[ "${result}" !=3D TEST ]]; then + return $KSFT_PASS + fi + + return $KSFT_FAIL +} + +test_diff_ns_global_vm_connect_to_global_host_ok() { + local ns0=3D"global0" + local ns1=3D"global1" + local port=3D12345 + local unixfile + local pidfile + local pids + + declare -a pids + + log_host "Setup socat bridge from ns ${ns0} to ns ${ns1} over port ${port= }" + + unixfile=3D$(mktemp -u /tmp/XXXX.sock) + + ip netns exec "${ns0}" \ + socat TCP-LISTEN:"${port}" UNIX-CONNECT:"${unixfile}" & + pids+=3D($!) + + ip netns exec "${ns1}" \ + socat UNIX-LISTEN:"${unixfile}" TCP-CONNECT:127.0.0.1:"${port}" & + pids+=3D($!) + + log_host "Launching ${VSOCK_TEST} in ns ${ns1}" + host_vsock_test "${ns1}" "server" "${VSOCK_CID}" "${port}" + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + if ! vm_start "${VSOCK_CID}" "${ns0}" "${pidfile}"; then + log_host "failed to start vm (cid=3D${cid}, ns=3D${ns0})" + terminate_pids "${pids[@]}" + rm -f "${unixfile}" + return $KSFT_FAIL + fi + + vm_wait_for_ssh "${ns0}" + vm_vsock_test "${ns0}" "client" "10.0.2.2" 2 "${port}" + rc=3D$? + + terminate_pidfiles "${pidfile}" + terminate_pids "${pids[@]}" + rm -f "${unixfile}" + + if [[ ! $rc -eq 0 ]]; then + return "${KSFT_FAIL}" + fi + + return "${KSFT_PASS}" + +} + +test_diff_ns_global_vm_connect_to_local_host_fails() { + local ns0=3D"global0" + local ns1=3D"local0" + local port=3D12345 + local pidfile + local result + local pid + + log_host "Launching socat in ns ${ns1}" + outfile=3D$(mktemp) + ip netns exec "${ns1}" socat VSOCK-LISTEN:${port} STDOUT &> "${outfile}" & + pid=3D$! + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + if ! vm_start "${VSOCK_CID}" "${ns0}" "${pidfile}"; then + log_host "failed to start vm (cid=3D${cid}, ns=3D${ns0})" + terminate_pids "${pid}" + rm -f "${outfile}" + return $KSFT_FAIL + fi + + vm_wait_for_ssh "${ns0}" + + vm_ssh "${ns0}" -- \ + bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_gue= st + + terminate_pidfiles "${pidfile}" + terminate_pids "${pid}" + + result=3D$(cat "${outfile}") + rm -f "${outfile}" + + if [[ "${result}" !=3D TEST ]]; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_diff_ns_local_host_connect_to_local_vm_fails() { + local ns0=3D"local0" + local ns1=3D"local1" + local port=3D12345 + local pidfile + local result + local pid + + outfile=3D$(mktemp) + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + if ! vm_start "${VSOCK_CID}" "${ns1}" "${pidfile}"; then + log_host "failed to start vm (cid=3D${cid}, ns=3D${ns0})" + return $KSFT_FAIL + fi + + vm_wait_for_ssh "${ns1}" + vm_ssh "${ns1}" -- socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}" & + echo TEST | ip netns exec "${ns0}" \ + socat STDIN VSOCK-CONNECT:"${VSOCK_CID}":"${port}" 2>/dev/null + + terminate_pidfiles "${pidfile}" + + result=3D$(cat "${outfile}") + rm -f "${outfile}" + + if [[ "${result}" !=3D TEST ]]; then + return $KSFT_PASS + fi + + return $KSFT_FAIL +} + +test_diff_ns_local_vm_connect_to_local_host_fails() { + local ns0=3D"local0" + local ns1=3D"local1" + local port=3D12345 + local pidfile + local result + local pid + + log_host "Launching socat in ns ${ns1}" + outfile=3D$(mktemp) + ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT &> "${outfile}= " & + pid=3D$! + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + if ! vm_start "${VSOCK_CID}" "${ns0}" "${pidfile}"; then + log_host "failed to start vm (cid=3D${cid}, ns=3D${ns0})" + rm -f "${outfile}" + return "${KSFT_FAIL}" + fi + + vm_wait_for_ssh "${ns0}" + + vm_ssh "${ns0}" -- \ + bash -c "echo TEST | socat STDIN VSOCK-CONNECT:2:${port}" 2>&1 | log_gue= st + + terminate_pidfiles "${pidfile}" + terminate_pids "${pid}" + + result=3D$(cat "${outfile}") + rm -f "${outfile}" + + if [[ "${result}" !=3D TEST ]]; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +__test_loopback_two_netns() { + local ns0=3D$1 + local ns1=3D$2 + local port=3D12345 + local result + local pid + + modprobe vsock_loopback &> /dev/null || : + + log_host "Launching socat in ns ${ns1}" + outfile=3D$(mktemp) + ip netns exec "${ns1}" socat VSOCK-LISTEN:"${port}" STDOUT > "${outfile}"= 2>/dev/null & + pid=3D$! + + log_host "Launching socat in ns ${ns0}" + echo TEST | ip netns exec "${ns0}" socat STDIN VSOCK-CONNECT:1:"${port}" = 2>/dev/null + terminate_pids "${pid}" + + result=3D$(cat "${outfile}") + rm -f "${outfile}" + + if [[ "${result}" =3D=3D TEST ]]; then + return 0 + fi + + return 1 +} + +test_diff_ns_global_to_local_loopback_local_fails() { + if ! __test_loopback_two_netns "global0" "local0"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_diff_ns_local_to_global_loopback_fails() { + if ! __test_loopback_two_netns "local0" "global0"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_diff_ns_local_to_local_loopback_fails() { + if ! __test_loopback_two_netns "local0" "local1"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_diff_ns_global_to_global_loopback_ok() { + if __test_loopback_two_netns "global0" "global1"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_same_ns_local_loopback_ok() { + if __test_loopback_two_netns "local0" "local0"; then + return "${KSFT_PASS}" + fi + + return "${KSFT_FAIL}" +} + +test_same_ns_local_host_connect_to_local_vm_ok() { + local ns=3D"local0" + local port=3D1234 + local pidfile + local rc + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + + if ! vm_start "${VSOCK_CID}" "${ns}" "${pidfile}"; then + return "${KSFT_FAIL}" + fi + + vm_vsock_test "${ns}" "server" 2 "${TEST_GUEST_PORT}" + host_vsock_test "${ns}" "client" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_= PORT}" + rc=3D$? + + terminate_pidfiles "${pidfile}" + + if [[ $rc -ne 0 ]]; then + return "${KSFT_FAIL}" + fi + + return "${KSFT_PASS}" +} + +test_same_ns_local_vm_connect_to_local_host_ok() { + local ns=3D"local0" + local port=3D1234 + local pidfile + local rc + + pidfile=3D$(mktemp /tmp/qemu_vsock_vmtest_XXXX.pid) + + if ! vm_start "${VSOCK_CID}" "${ns}" "${pidfile}"; then + return "${KSFT_FAIL}" + fi + + vm_vsock_test "${ns}" "server" 2 "${TEST_GUEST_PORT}" + host_vsock_test "${ns}" "client" "127.0.0.1" "${VSOCK_CID}" "${TEST_HOST_= PORT}" + rc=3D$? + + terminate_pidfiles "${pidfile}" + + if [[ $rc -ne 0 ]]; then + return "${KSFT_FAIL}" + fi + + return "${KSFT_PASS}" +} + +shared_vm_test() { + local tname + + tname=3D"${1}" + + for testname in "${USE_SHARED_VM[@]}"; do + if [[ "${tname}" =3D=3D "${testname}" ]]; then + return 0 + fi + done + + return 1 +} + + +init_netns_test() { + local tname + + tname=3D"${1}" + + for testname in "${USE_INIT_NETNS[@]}"; do + if [[ "${tname}" =3D=3D "${testname}" ]]; then + return 0 + fi + done + + return 1 +} + +check_result() { + local rc num + + rc=3D$1 + num=3D$(( cnt_total + 1 )) + + if [[ ${rc} -eq $KSFT_PASS ]]; then + cnt_pass=3D$(( cnt_pass + 1 )) + echo "ok ${num} ${arg}" + elif [[ ${rc} -eq $KSFT_SKIP ]]; then + cnt_skip=3D$(( cnt_skip + 1 )) + echo "ok ${num} ${arg} # SKIP" + elif [[ ${rc} -eq $KSFT_FAIL ]]; then + cnt_fail=3D$(( cnt_fail + 1 )) + echo "not ok ${num} ${arg} # exit=3D$rc" + fi + + cnt_total=3D$(( cnt_total + 1 )) } =20 -run_test() { +run_shared_vm_tests() { + local start_shared_vm pidfile local host_oops_cnt_before local host_warn_cnt_before local vm_oops_cnt_before @@ -483,42 +1191,99 @@ run_test() { local name local rc =20 - host_oops_cnt_before=3D$(dmesg | grep -c -i 'Oops') - host_warn_cnt_before=3D$(dmesg --level=3Dwarn | wc -l) - vm_oops_cnt_before=3D$(vm_ssh -- dmesg | grep -c -i 'Oops') - vm_warn_cnt_before=3D$(vm_ssh -- dmesg --level=3Dwarn | wc -l) + start_shared_vm=3D0 =20 - name=3D$(echo "${1}" | awk '{ print $1 }') - eval test_"${name}" - rc=3D$? + for arg in "${ARGS[@]}"; do + if shared_vm_test "${arg}"; then + start_shared_vm=3D1 + break + fi + done =20 - host_oops_cnt_after=3D$(dmesg | grep -i 'Oops' | wc -l) - if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then - echo "FAIL: kernel oops detected on host" | log_host "${name}" - rc=3D$KSFT_FAIL + pidfile=3D"" + if [[ "${start_shared_vm}" =3D=3D 1 ]]; then + pidfile=3D$(mktemp $PIDFILE_TEMPLATE) + log_host "Booting up VM" + vm_start "${VSOCK_CID}" "none" "${pidfile}" + vm_wait_for_ssh "none" + log_host "VM booted up" fi =20 - host_warn_cnt_after=3D$(dmesg --level=3Dwarn | wc -l) - if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then - echo "FAIL: kernel warning detected on host" | log_host "${name}" - rc=3D$KSFT_FAIL - fi + for arg in "${ARGS[@]}"; do + if ! shared_vm_test "${arg}"; then + continue + fi =20 - vm_oops_cnt_after=3D$(vm_ssh -- dmesg | grep -i 'Oops' | wc -l) - if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then - echo "FAIL: kernel oops detected on vm" | log_host "${name}" - rc=3D$KSFT_FAIL - fi + if ! check_test_deps "${arg}"; then + log_host "Skipping ${arg}" + check_result "${KSFT_SKIP}" + continue + fi + + host_oops_cnt_before=3D$(dmesg | grep -c -i 'Oops') + host_warn_cnt_before=3D$(dmesg --level=3Dwarn | wc -l) + vm_oops_cnt_before=3D$(vm_ssh none -- dmesg | grep -c -i 'Oops') + vm_warn_cnt_before=3D$(vm_ssh none -- dmesg --level=3Dwarn | wc -l) + + name=3D$(echo "${arg}" | awk '{ print $1 }') + log_host "Executing test_${name}" + eval test_"${name}" + rc=3D$? + + host_oops_cnt_after=3D$(dmesg | grep -i 'Oops' | wc -l) + if [[ ${host_oops_cnt_after} -gt ${host_oops_cnt_before} ]]; then + echo "FAIL: kernel oops detected on host" | log_host "${name}" + rc=3D$KSFT_FAIL + fi =20 - vm_warn_cnt_after=3D$(vm_ssh -- dmesg --level=3Dwarn | wc -l) - if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then - echo "FAIL: kernel warning detected on vm" | log_host "${name}" - rc=3D$KSFT_FAIL + host_warn_cnt_after=3D$(dmesg --level=3Dwarn | wc -l) + if [[ ${host_warn_cnt_after} -gt ${host_warn_cnt_before} ]]; then + echo "FAIL: kernel warning detected on host" | log_host "${name}" + rc=3D$KSFT_FAIL + fi + + vm_oops_cnt_after=3D$(vm_ssh none -- dmesg | grep -i 'Oops' | wc -l) + if [[ ${vm_oops_cnt_after} -gt ${vm_oops_cnt_before} ]]; then + echo "FAIL: kernel oops detected on vm" | log_host "${name}" + rc=3D$KSFT_FAIL + fi + + vm_warn_cnt_after=3D$(vm_ssh none -- dmesg --level=3Dwarn | wc -l) + if [[ ${vm_warn_cnt_after} -gt ${vm_warn_cnt_before} ]]; then + echo "FAIL: kernel warning detected on vm" | log_host "${name}" + rc=3D$KSFT_FAIL + fi + + check_result "${rc}" + done + + if [[ -n "${pidfile}" ]]; then + log_host "VM terminate" + terminate_pidfiles "${pidfile}" fi +} + +run_isolated_vm_tests() { + for arg in "${ARGS[@]}"; do + if shared_vm_test "${arg}"; then + continue + fi + + add_namespaces + if init_netns_test "${arg}"; then + init_namespaces + fi =20 - return "${rc}" + name=3D$(echo "${arg}" | awk '{ print $1 }') + log_host "Executing test_${name}" + eval test_"${name}" + check_result $? + + del_namespaces + done } =20 +BUILD=3D0 QEMU=3D"qemu-system-$(uname -m)" =20 while getopts :hvsq:b o @@ -543,34 +1308,13 @@ fi check_args "${ARGS[@]}" check_deps check_vng +check_socat handle_build =20 echo "1..${#ARGS[@]}" =20 -log_host "Booting up VM" -vm_start -vm_wait_for_ssh -log_host "VM booted up" - -cnt_pass=3D0 -cnt_fail=3D0 -cnt_skip=3D0 -cnt_total=3D0 -for arg in "${ARGS[@]}"; do - run_test "${arg}" - rc=3D$? - if [[ ${rc} -eq $KSFT_PASS ]]; then - cnt_pass=3D$(( cnt_pass + 1 )) - echo "ok ${cnt_total} ${arg}" - elif [[ ${rc} -eq $KSFT_SKIP ]]; then - cnt_skip=3D$(( cnt_skip + 1 )) - echo "ok ${cnt_total} ${arg} # SKIP" - elif [[ ${rc} -eq $KSFT_FAIL ]]; then - cnt_fail=3D$(( cnt_fail + 1 )) - echo "not ok ${cnt_total} ${arg} # exit=3D$rc" - fi - cnt_total=3D$(( cnt_total + 1 )) -done +run_shared_vm_tests +run_isolated_vm_tests =20 echo "SUMMARY: PASS=3D${cnt_pass} SKIP=3D${cnt_skip} FAIL=3D${cnt_fail}" echo "Log: ${LOG}" --=20 2.47.3