* [PATCH v4 1/3] tests/vhost-user-bridge: Move to contrib/vhost-user-bridge/
2026-01-29 13:30 [PATCH v4 0/3] vhost-user-bridge housekeeping and test Yodel Eldar
@ 2026-01-29 13:30 ` Yodel Eldar
2026-01-29 13:30 ` [PATCH v4 2/3] tests/functional/x86_64: Add vhost-user-bridge test Yodel Eldar
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Yodel Eldar @ 2026-01-29 13:30 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Paolo Bonzini,
Marc-André Lureau, Thomas Huth, Cédric Le Goater,
Yodel Eldar
After the introduction of vhost-user-bridge and libvhost-user, we
formed the convention of placing vhost-user daemons in eponymous subdirs
of contrib/. Follow this convention.
Create a contrib/vhost-user-bridge/ directory and move vhost-user-bridge
into it. Extract its build target definition from tests/meson.build into
the new directory, and include its subdir in the root-level meson.build.
Add a section about it in the "vhost-user daemons in contrib" document.
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
---
contrib/vhost-user-bridge/meson.build | 4 ++
.../vhost-user-bridge}/vhost-user-bridge.c | 0
.../devices/virtio/vhost-user-contrib.rst | 39 +++++++++++++++++++
meson.build | 1 +
tests/meson.build | 6 ---
5 files changed, 44 insertions(+), 6 deletions(-)
create mode 100644 contrib/vhost-user-bridge/meson.build
rename {tests => contrib/vhost-user-bridge}/vhost-user-bridge.c (100%)
diff --git a/contrib/vhost-user-bridge/meson.build b/contrib/vhost-user-bridge/meson.build
new file mode 100644
index 0000000000..aa58c1df20
--- /dev/null
+++ b/contrib/vhost-user-bridge/meson.build
@@ -0,0 +1,4 @@
+if have_tools and have_vhost_user and host_os == 'linux'
+ executable('vhost-user-bridge', files('vhost-user-bridge.c'),
+ dependencies: [qemuutil, vhost_user], install: false)
+endif
diff --git a/tests/vhost-user-bridge.c b/contrib/vhost-user-bridge/vhost-user-bridge.c
similarity index 100%
rename from tests/vhost-user-bridge.c
rename to contrib/vhost-user-bridge/vhost-user-bridge.c
diff --git a/docs/system/devices/virtio/vhost-user-contrib.rst b/docs/system/devices/virtio/vhost-user-contrib.rst
index 48d04d2ade..660d29a700 100644
--- a/docs/system/devices/virtio/vhost-user-contrib.rst
+++ b/docs/system/devices/virtio/vhost-user-contrib.rst
@@ -85,3 +85,42 @@ vhost-user-scsi - SCSI controller
The vhost-user-scsi daemon can proxy iSCSI devices onto a virtualized
SCSI controller.
+
+.. _vhost_user_bridge:
+
+vhost-user-bridge - Network bridge
+==================================
+
+The vhost-user-bridge daemon serves as a development tool for testing real
+internet traffic by providing a networking backend, i.e. server, for the
+vhost-user protocol.
+
+Example
+-------
+For a single QEMU instance that both runs the user-mode net stack (slirp) and
+serves as a vhost-user protocol frontend, i.e. client, simultaneously:
+
+First, start vhost-user-bridge:
+
+::
+
+ $ vhost-user-bridge -u /tmp/vubr.sock \
+ -l 127.0.0.1:4444 \
+ -r 127.0.0.1:5555
+
+Then, invoke QEMU:
+
+::
+
+ $ qemu-system-x86_64 \
+ -m 4G \
+ -object memory-backend-memfd,id=mem0,size=4G,share=on,prealloc=on \
+ -numa node,memdev=mem0 \
+ -chardev socket,id=char0,path=/tmp/vubr.sock \
+ -netdev vhost-user,id=vhost0,chardev=char0,vhostforce=on \
+ -device virtio-net-pci,netdev=vhost0 \
+ -netdev socket,id=udp0,udp=localhost:4444,localaddr=localhost:5555 \
+ -netdev user,id=user0 \
+ -netdev hubport,id=hub0,hubid=0,netdev=udp0 \
+ -netdev hubport,id=hub1,hubid=0,netdev=user0 \
+ ...
diff --git a/meson.build b/meson.build
index cc0dfed066..bca61aae65 100644
--- a/meson.build
+++ b/meson.build
@@ -4494,6 +4494,7 @@ if have_tools
if have_vhost_user
subdir('contrib/vhost-user-blk')
+ subdir('contrib/vhost-user-bridge')
subdir('contrib/vhost-user-gpu')
subdir('contrib/vhost-user-input')
subdir('contrib/vhost-user-scsi')
diff --git a/tests/meson.build b/tests/meson.build
index cbe7916241..87861b2857 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -70,12 +70,6 @@ test_deps = {
'test-qht-par': qht_bench,
}
-if have_tools and have_vhost_user and host_os == 'linux'
- executable('vhost-user-bridge',
- sources: files('vhost-user-bridge.c'),
- dependencies: [qemuutil, vhost_user])
-endif
-
subdir('decode')
if 'CONFIG_TCG' in config_all_accel
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v4 2/3] tests/functional/x86_64: Add vhost-user-bridge test
2026-01-29 13:30 [PATCH v4 0/3] vhost-user-bridge housekeeping and test Yodel Eldar
2026-01-29 13:30 ` [PATCH v4 1/3] tests/vhost-user-bridge: Move to contrib/vhost-user-bridge/ Yodel Eldar
@ 2026-01-29 13:30 ` Yodel Eldar
2026-01-29 13:30 ` [PATCH v4 3/3] contrib/vhost-user-bridge: Add UDP receive hexdump Yodel Eldar
2026-02-09 16:08 ` [PATCH v4 0/3] vhost-user-bridge housekeeping and test Yodel Eldar
3 siblings, 0 replies; 8+ messages in thread
From: Yodel Eldar @ 2026-01-29 13:30 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Paolo Bonzini,
Marc-André Lureau, Thomas Huth, Cédric Le Goater,
Yodel Eldar, Marc-André Lureau
Introduce a functional test of vhost-user-bridge and enter it into
MAINTAINERS under the vhost section.
The test runs vhost-user-bridge as a subprocess, then launches a guest
with four backends: a unix domain socket for vhost-user, a UDP socket, a
user-mode net, and a hubport to hub the UDP and user backends; only the
vhost-user backend is exposed, the rest are deviceless. This
configuration mimics the testing setup described in the initial commit
of vhost-user-bridge in 3595e2eb0a23.
The test creates a scratch file containing a hardcoded UUID on the host
and exposes it to the the guest via the tftp parameter of the user
netdev. After the guest invokes tftp to request the file, the test
verifies the transfer by hashsum.
Similarly, the test creates a file with another hardcoded UUID in the
guest. A call to check_http_download() serves the file to the host via
http, whereupon a check of the file hashsum occurs on the host.
Lastly, add the test to the thorough tests suite in meson.build.
Suggested-by: Cédric Le Goater <clg@redhat.com>
Suggested-by: Marc-André Lureau <marcandre.lureau@gmail.com>
Suggested-by: Michael S. Tsirkin <mst@redhat.com>
Suggested-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
---
MAINTAINERS | 1 +
tests/functional/x86_64/meson.build | 1 +
.../x86_64/test_vhost_user_bridge.py | 147 ++++++++++++++++++
3 files changed, 149 insertions(+)
create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
diff --git a/MAINTAINERS b/MAINTAINERS
index dccdf47888..234b076397 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2395,6 +2395,7 @@ F: subprojects/libvhost-user/
F: block/export/vhost-user*
F: util/vhost-user-server.c
F: net/vhost*
+F: tests/functional/x86_64/test_vhost_user_bridge.py
vhost-shadow-virtqueue
R: Eugenio Pérez <eperezma@redhat.com>
diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
index f78eec5e6c..beab4f304b 100644
--- a/tests/functional/x86_64/meson.build
+++ b/tests/functional/x86_64/meson.build
@@ -34,6 +34,7 @@ tests_x86_64_system_thorough = [
'reverse_debug',
'tuxrun',
'vfio_user_client',
+ 'vhost_user_bridge',
'virtio_balloon',
'virtio_gpu',
]
diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
new file mode 100755
index 0000000000..c36c625420
--- /dev/null
+++ b/tests/functional/x86_64/test_vhost_user_bridge.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2025 Software Freedom Conservancy, Inc.
+#
+# Author: Yodel Eldar <yodel.eldar@yodel.dev>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Test vhost-user-bridge (vubr) functionality:
+
+ 1) Run vhost-user-bridge on the host.
+ 2) Launch a guest VM:
+ a) Instantiate a unix domain socket to the vubr-created path
+ b) Instantiate a vhost-user backend on top of that socket
+ c) Map a virtio-net-pci device to the vhost-user backend
+ d) Instantiate a UDP socket backend
+ e) Instantiate a user-mode net backend
+ i) Forward an ephemeral port to port 8080 in-guest with hostfwd=
+ ii) Expose a generated scratch file to the guest with tftp=
+ f) Hub the UDP and user-mode backends.
+ 3) Invoke tftp in the guest to download exported scratch file from the host.
+ 4) Serve a file to the host via http server in the guest.
+"""
+
+import os
+import shutil
+import subprocess
+from qemu_test import Asset, LinuxKernelTest, which
+from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import is_readable_executable_file
+from qemu_test import wait_for_console_pattern
+from qemu_test.ports import Ports
+
+class VhostUserBridge(LinuxKernelTest):
+
+ ASSET_KERNEL_INITRAMFS = Asset(
+ "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
+ "8860d7aa59434f483542cdf25b42eacae0d4d4aa7ec923af9589d1ad4703d42b")
+
+ HOST_UUID = "ba4c2e39-627f-487d-ae3b-93cc5d783eb8"
+ HOST_UUID_HSUM = \
+ "d2932e34bf6c17b33e7325140b691e27c191d9ac4dfa550f68c09506facb09b9"
+
+ GUEST_UUID = "143d2b21-fdf0-4c5e-a9ef-f35ebbac8945"
+ GUEST_UUID_HSUM = \
+ "14b64203f5cf2afe520f8be0fdfe630aafc1e85d1301f55a0d1681e68881f3a2"
+
+ def configure_vm(self, ud_socket_path, lport, rport, hostfwd_port, tftpdir):
+ self.require_accelerator("kvm")
+ self.require_netdev("vhost-user")
+ self.require_netdev("socket")
+ self.require_netdev("hubport")
+ self.require_netdev("user")
+ self.require_device("virtio-net-pci")
+ self.set_machine("q35")
+ self.vm.add_args(
+ "-cpu", "host",
+ "-accel", "kvm",
+ "-append", "printk.time=0 console=ttyS0",
+ "-smp", "2",
+ "-m", "128M",
+ "-object", "memory-backend-memfd,id=mem0,"
+ "size=128M,share=on,prealloc=on",
+ "-numa", "node,memdev=mem0",
+ "-chardev", f"socket,id=char0,path={ud_socket_path}",
+ "-netdev", "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
+ "-device", "virtio-net-pci,netdev=vhost0",
+ "-netdev", f"socket,id=udp0,udp=localhost:{lport},"
+ f"localaddr=localhost:{rport}",
+ "-netdev", "hubport,id=hub0,hubid=0,netdev=udp0",
+ "-netdev", f"user,id=user0,tftp={tftpdir},"
+ f"hostfwd=tcp:127.0.0.1:{hostfwd_port}-:8080",
+ "-netdev", "hubport,id=hub1,hubid=0,netdev=user0"
+ )
+
+ def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
+ vubr_args = []
+
+ if (stdbuf_path := which("stdbuf")) is None:
+ self.log.info("Could not find stdbuf: vhost-user-bridge "
+ "log lines may appear out of order")
+ else:
+ vubr_args += [stdbuf_path, "-o0", "-e0"]
+
+ vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
+ "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
+
+ return vubr_args
+
+ def test_vhost_user_bridge(self):
+ prompt = "~ # "
+ host_uuid_filename = "vubr-test-uuid.txt"
+ guest_uuid_path = "/tmp/uuid.txt"
+ kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
+
+ vubr_path = self.build_file("contrib", "vhost-user-bridge",
+ "vhost-user-bridge")
+ if not is_readable_executable_file(vubr_path):
+ self.skipTest("Could not find a readable and executable "
+ "vhost-user-bridge")
+
+ vubr_log_path = self.log_file("vhost-user-bridge.log")
+ self.log.info("For the vhost-user-bridge application log,"
+ f" see: {vubr_log_path}")
+
+ sock_dir = self.socket_dir()
+ ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
+
+ tftpdir = self.scratch_file("tftp")
+ shutil.rmtree(tftpdir, ignore_errors=True)
+ os.mkdir(tftpdir)
+ host_uuid_path = self.scratch_file("tftp", host_uuid_filename)
+ with open(host_uuid_path, "w", encoding="utf-8") as host_uuid_file:
+ host_uuid_file.write(self.HOST_UUID)
+
+ with Ports() as ports:
+ # pylint: disable=unbalanced-tuple-unpacking
+ lport, rport, hostfwd_port = ports.find_free_ports(3)
+
+ self.configure_vm(ud_socket_path, lport, rport, hostfwd_port,
+ tftpdir)
+
+ vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
+ lport, rport)
+
+ with open(vubr_log_path, "w", encoding="utf-8") as vubr_log, \
+ subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
+ stdout=vubr_log,
+ stderr=subprocess.STDOUT) as vubr_proc:
+ self.launch_kernel(kernel_path, wait_for=prompt)
+
+ exec_command_and_wait_for_pattern(self,
+ f"tftp -g -r {host_uuid_filename} 10.0.2.2 ; "
+ f"sha256sum {host_uuid_filename}", self.HOST_UUID_HSUM)
+ wait_for_console_pattern(self, prompt)
+
+ exec_command_and_wait_for_pattern(self,
+ f"echo -n '{self.GUEST_UUID}' > {guest_uuid_path}", prompt)
+ self.check_http_download(guest_uuid_path, self.GUEST_UUID_HSUM)
+ wait_for_console_pattern(self, prompt)
+
+ self.vm.shutdown()
+ vubr_proc.terminate()
+ vubr_proc.wait()
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v4 0/3] vhost-user-bridge housekeeping and test
2026-01-29 13:30 [PATCH v4 0/3] vhost-user-bridge housekeeping and test Yodel Eldar
` (2 preceding siblings ...)
2026-01-29 13:30 ` [PATCH v4 3/3] contrib/vhost-user-bridge: Add UDP receive hexdump Yodel Eldar
@ 2026-02-09 16:08 ` Yodel Eldar
2026-02-17 14:56 ` Yodel Eldar
3 siblings, 1 reply; 8+ messages in thread
From: Yodel Eldar @ 2026-02-09 16:08 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Stefano Garzarella, Paolo Bonzini,
Marc-André Lureau, Thomas Huth, Cédric Le Goater
Hi,
On 29/01/2026 07:30, Yodel Eldar wrote:
> Hi,
>
> The existence check of the vhost-user-bridge file checks for None
> when it should check for False to determine whether the test should be
> skipped; a trivial fix.
>
> Changes since v3:
> --------------------
> - Fix is_readable_executable_file() check of vubr for skipping test
>
> Link to v3:
> -----------
> https://lore.kernel.org/qemu-devel/20260120215021.30711-1-yodel.eldar@yodel.dev/
>
> Thanks,
> Yodel
>
> Cover letter from v3:
> =====================
>
> This series (1) moves vhost-user-bridge (vubr) from tests/ to
> contrib/vhost-user-bridge/, (2) introduces a basic functional test of
> vubr, and (3) adds UDP receive packet logging to vubr:
>
> Patch 1: Moving vhost-user-bridge
> ---------------------------------
> After the introduction of vhost-user-bridge and libvhost-user, new
> vhost-user backend daemons were placed in dedicated subdirectories of
> contrib/. An F: line under the vhost section in MAINTAINERS grants these
> daemons coverage; in tests/, however, vhost-user-bridge lacks coverage
> and appears orphaned there. So, let's give it a home with the rest of
> the vhost-user backend family.
>
> As part of the move, vhost-user-bridge also gains a section in the
> vhost-user-contrib document. More can be said about vubr there, like its
> client mode feature added in aef8486ede8b by Marc-André, but for now a
> short description and usage example will help folks interested in using
> the daemon to expand vhost-user support in QEMU.
>
> Lastly, Patch 1 makes the necessary changes to the meson build files in
> tests/ and root, and the new one in contrib/vhost-user-bridge/.
>
> Patch 2: Introducing a functional test
> --------------------------------------
> This test was inspired by discussions with Cédric regarding build errors
> from recent glibc const changes and was the subject of the first RFC.
>
> The functional test introduced in the second patch starts vubr as a
> subprocess, then launches a single QEMU instance that provides a slirp
> stack and serves as the frontend of the vhost-user protocol, much like
> the testing setup described in vhost-user-bridge's initial commit in
> 3595e2eb0a23.
>
> For additional details about the test, please see Patch 2's commit
> message and/or the test's header; in short: the host serves a file to
> the guest via tftp, then the guest serves a file to the host via http.
>
> The guest's kernel image was built with Buildroot in a Containerfile
> that is based on a snapshot Debian container image, so that the kernel
> image remains bit-for-bit reproducible indefinitely. For the image and
> its build files, please see:
> https://github.com/yodel/vhost-user-bridge-test
> The repo will be placed in archive (read-only) mode if/when the series
> is pulled.
>
> Patch 3: Adding UDP RX logging
> ------------------------------
> An existing transmit hexdump of UDP packets provides valuable data;
> adding a receive hexdump to complement TX doubles the fun (read:
> visibility). Like TX, the RX hexdump is gated behind a currently
> hardcoded-on macro. Another new debug print separates transmit blocks
> from receives.
>
> Changes since RFCv2:
> --------------------
> - Promoted to PATCH
> - Collect R-b's (thanks to Marc-André and Thomas)
> - Add test to .../x86_64/meson.build thorough suite (Marc-André)
> - Add encoding= args to open() (Thomas)
> - Add pylint disable=unbalanced-tuple-unpacking on find_free_ports()
> - Minor commit message rewording
>
> Changes since RFCv1:
> --------------------
> - Remove external host dependency (Marc-André)
> - Use user netdev tftp feature (Thomas)
> - Use check_http_download (Thomas)
> - Remove wget
> - Inherit from LinuxKernelTest instead of QemuSystemTest
> - Use hardcoded UUIDs in files on the guest and host for data transfer
> - Use a Popen context manager to terminate and wait on vubr
> - Add test to MAINTAINERS under the vhost section (Michael)
> - New patch to move from tests/ to contrib/vhost-user-bridge/ and add
> docs section
> - New patch to add RX debug logging in vhost-user-bridge.c
>
> Links
> -----
> RFCv2:
> https://lore.kernel.org/qemu-devel/20260112230127.99125-1-yodel.eldar@yodel.dev/
> RFCv1:
> https://lore.kernel.org/qemu-devel/20251230002604.113632-1-yodel.eldar@yodel.dev/
>
> Yodel Eldar (3):
> tests/vhost-user-bridge: Move to contrib/vhost-user-bridge/
> tests/functional/x86_64: Add vhost-user-bridge test
> contrib/vhost-user-bridge: Add UDP receive hexdump
>
> MAINTAINERS | 1 +
> contrib/vhost-user-bridge/meson.build | 4 +
> .../vhost-user-bridge}/vhost-user-bridge.c | 7 +
> .../devices/virtio/vhost-user-contrib.rst | 39 +++++
> meson.build | 1 +
> tests/functional/x86_64/meson.build | 1 +
> .../x86_64/test_vhost_user_bridge.py | 147 ++++++++++++++++++
> tests/meson.build | 6 -
> 8 files changed, 200 insertions(+), 6 deletions(-)
> create mode 100644 contrib/vhost-user-bridge/meson.build
> rename {tests => contrib/vhost-user-bridge}/vhost-user-bridge.c (98%)
> create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>
Ping?
Note: All patches have been reviewed.
Link to v4:
https://lore.kernel.org/qemu-devel/20260129133049.119829-1-yodel.eldar@yodel.dev/
Thanks,
Yodel
^ permalink raw reply [flat|nested] 8+ messages in thread