[v4,3/3] dts: add virtio forwarding test suite
Checks
Commit Message
Add test suite covering virtio-user/vhost-user
server/client forwarding scenarios with
testpmd packet validation.
Signed-off-by: Dean Marx <dmarx@iol.unh.edu>
---
doc/api/dts/tests.TestSuite_virtio_fwd.rst | 8 +
dts/tests/TestSuite_virtio_fwd.py | 193 +++++++++++++++++++++
2 files changed, 201 insertions(+)
create mode 100644 doc/api/dts/tests.TestSuite_virtio_fwd.rst
create mode 100644 dts/tests/TestSuite_virtio_fwd.py
Comments
On Fri, Oct 24, 2025 at 2:51 PM Dean Marx <dmarx@iol.unh.edu> wrote:
>
> + @requires(topology_type=LinkTopology.TWO_LINKS)
> + @func_test
> + def pvp_loop(self) -> None:
> + """Test vhost/virtio physical-virtual-physical topology.
> +
> + Steps:
> + * Launch testpmd session with a physical NIC and virtio-user
> vdev
> + connected to a vhost-net socket.
> + * Configure the tap interface that is created with IP address
> and
> + set link state to UP.
> + * Launch second testpmd session with af_packet vdev connected
> to
> + the tap interface.
> + * Start packet forwarding on both testpmd sessions.
> + * Send 100 packets to the physical interface from external
> tester.
> + * Capture packets on the same physical interface.
> +
> + Verify:
> + * Vhost session receives/forwards 100+ packets.
> + * Physical interface receives all 100 sent packets.
> + """
> + self.sut_node = self._ctx.sut_node
> + if not isinstance(self.sut_node.main_session, LinuxSession):
> + verify(False, "Must be running on a Linux environment.")
> + with TestPmd(
> + prefix="virtio",
> +
> vdevs=[VirtualDevice("virtio_user0,path=/dev/vhost-net,queues=1,queue_size=1024")],
> + ) as virtio:
> + self.sut_node.main_session.set_interface_link_up(name="tap0")
>
Is it the case that if I already have tap0 created on my SUT machine, this
testsuite will create a tap1 and then the testsuite will fail to run
properly on the line above (because tap1 won't be brought up)? If there is
no way of getting around this limitation, we should add a new capability
for "tap interface names available" or something and skip if tap0 is found
in "ip a" or a similar solution.
> + with TestPmd(
> + prefix="vhost", no_pci=True,
> vdevs=[VirtualDevice("net_af_packet0,iface=tap0")]
> + ) as vhost:
> + virtio.set_forward_mode(SimpleForwardingModes.mac)
> + vhost.set_forward_mode(SimpleForwardingModes.mac)
> + vhost.start()
> + virtio.start()
> +
> + packet = Ether() / IP()
> + packets = [packet] * 100
> + captured_packets = send_packets_and_capture(packets)
> +
> + vhost.stop()
> + virtio.stop()
> +
> + vhost_forwarding_stats, vhost_raw_output =
> vhost.show_port_stats_all()
> +
> + rx_packets = vhost_forwarding_stats[0].rx_packets
> + tx_packets = vhost_forwarding_stats[0].tx_packets
> +
> + log(f"Vhost forwarding statistics:\n{vhost_raw_output}")
> +
> + verify(
> + rx_packets >= 100 and tx_packets >= 100,
> + f"PVP loop forwarding verification failed: vhost
> interface RX={rx_packets},"
> + f" TX={tx_packets} (expected ≥100 each).",
> + )
> +
> + verify(
> + len(captured_packets) >= 100, "Sent packets not
> received on physical interface."
> + )
> --
> 2.51.0
>
>
Otherwise, change the pvp requirement to ONE_LINK, set portlist to 0, 2, 1
(2 is the vdev) when 2 link topology, and 0, 1 when 1 link, and set
forwarding to chained mode so that the packet forwarding works as expected
regardless of whether we have 1 or 2 physical interfaces the testpmd
instance is using.
Thanks.
Reviewed-by: Patrick Robb <probb@iol.unh.edu>
On Fri, Nov 7, 2025 at 3:49 PM Patrick Robb <probb@iol.unh.edu> wrote:
>
>
>
> Is it the case that if I already have tap0 created on my SUT machine, this testsuite will create a tap1 and then the testsuite will fail to run properly on the line above (because tap1 won't be brought up)? If there is no way of getting around this limitation, we should add a new capability for "tap interface names available" or something and skip if tap0 is found in "ip a" or a similar solution.
Yes, I've added a workaround which sends a delete tap0 command prior
to the testpmd session starting. If the tap0 interface doesn't exist
it fails silently and continues with the rest of the test case.
>>
>
>
> Otherwise, change the pvp requirement to ONE_LINK, set portlist to 0, 2, 1 (2 is the vdev) when 2 link topology, and 0, 1 when 1 link, and set forwarding to chained mode so that the packet forwarding works as expected regardless of whether we have 1 or 2 physical interfaces the testpmd instance is using.
Done
>
> Thanks.
>
> Reviewed-by: Patrick Robb <probb@iol.unh.edu>
Thanks for the review!
new file mode 100644
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+virtio_fwd Test Suite
+=====================
+
+.. automodule:: tests.TestSuite_virtio_fwd
+ :members:
+ :show-inheritance:
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,193 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 University of New Hampshire
+
+"""Virtio forwarding test suite.
+
+Verify vhost/virtio pvp and fully virtual functionalities.
+"""
+
+from scapy.layers.inet import IP
+from scapy.layers.l2 import Ether
+
+from api.capabilities import LinkTopology
+from api.packet import send_packets_and_capture
+from api.test import log, verify
+from api.testpmd import TestPmd
+from api.testpmd.config import SimpleForwardingModes
+from framework.test_suite import TestSuite, func_test
+from framework.testbed_model.capability import requires
+from framework.testbed_model.linux_session import LinuxSession
+from framework.testbed_model.virtual_device import VirtualDevice
+
+
+class TestVirtioFwd(TestSuite):
+ """Virtio forwarding test suite."""
+
+ virtio_user_vdev = VirtualDevice(
+ "net_virtio_user0,mac=00:01:02:03:04:05,path=/tmp/vhost-net,server=1"
+ )
+ vhost_user_vdev = VirtualDevice("eth_vhost0,iface=/tmp/vhost-net,client=1")
+
+ @requires(topology_type=LinkTopology.NO_LINK)
+ @func_test
+ def virtio_server(self) -> None:
+ """Test virtio server packet transmission.
+
+ Steps:
+ * Launch a testpmd session with a vhost-user virtual device (client side).
+ * Launch a testpmd session with a virtio-user virtual device (server side).
+ * Set the forwarding mode to mac in both sessions.
+ * Start packet forwarding on vhost session.
+ * Send a burst of packets from the virtio session.
+ * Stop packet forwarding on vhost session and collect packet stats.
+
+ Verify:
+ * Vhost session receives packets from virtio session.
+ """
+ with (
+ TestPmd(
+ prefix="vhost",
+ no_pci=True,
+ memory_channels=4,
+ vdevs=[self.vhost_user_vdev],
+ ) as vhost,
+ TestPmd(
+ prefix="virtio",
+ no_pci=True,
+ memory_channels=4,
+ vdevs=[self.virtio_user_vdev],
+ ) as virtio,
+ ):
+ vhost.set_forward_mode(SimpleForwardingModes.mac)
+ virtio.set_forward_mode(SimpleForwardingModes.mac)
+
+ vhost.start()
+ virtio.start_tx_first(burst_num=32)
+ vhost.stop()
+
+ vhost_forwarding_stats, vhost_raw_output = vhost.show_port_stats_all()
+
+ rx_packets = vhost_forwarding_stats[0].rx_packets
+ tx_packets = vhost_forwarding_stats[0].tx_packets
+
+ log(f"Vhost forwarding statistics:\n{vhost_raw_output}")
+
+ verify(
+ rx_packets != 0 and tx_packets != 0,
+ "Vhost session failed to receive packets from virtio session.",
+ )
+
+ @requires(topology_type=LinkTopology.NO_LINK)
+ @func_test
+ def virtio_server_reconnect(self) -> None:
+ """Test virtio server reconnection.
+
+ Steps:
+ * Launch a testpmd session with a vhost-user virtual device (client side).
+ * Launch a testpmd session with a virtio-user virtual device (server side).
+ * Close the virtio session and relaunch it.
+ * Start packet forwarding on vhost session.
+ * Send a burst of packets from the virtio session.
+ * Stop packet forwarding on vhost session and collect packet stats.
+
+ Verify:
+ * Vhost session receives packets from relaunched virtio session.
+ """
+ with TestPmd(
+ prefix="vhost",
+ no_pci=True,
+ memory_channels=4,
+ vdevs=[self.vhost_user_vdev],
+ ) as vhost:
+ with TestPmd(
+ prefix="virtio",
+ no_pci=True,
+ memory_channels=4,
+ vdevs=[self.virtio_user_vdev],
+ ) as virtio:
+ pass
+ # end session and reconnect
+ with TestPmd(
+ prefix="virtio",
+ no_pci=True,
+ memory_channels=4,
+ vdevs=[self.virtio_user_vdev],
+ ) as virtio:
+ virtio.set_forward_mode(SimpleForwardingModes.mac)
+ vhost.set_forward_mode(SimpleForwardingModes.mac)
+
+ vhost.start()
+ virtio.start_tx_first(burst_num=32)
+ vhost.stop()
+
+ vhost_forwarding_stats, vhost_raw_output = vhost.show_port_stats_all()
+
+ rx_packets = vhost_forwarding_stats[0].rx_packets
+ tx_packets = vhost_forwarding_stats[0].tx_packets
+
+ log(f"Vhost forwarding statistics:\n{vhost_raw_output}")
+
+ verify(
+ rx_packets != 0 and tx_packets != 0,
+ "Vhost session failed to receive packets from virtio session.",
+ )
+
+ @requires(topology_type=LinkTopology.TWO_LINKS)
+ @func_test
+ def pvp_loop(self) -> None:
+ """Test vhost/virtio physical-virtual-physical topology.
+
+ Steps:
+ * Launch testpmd session with a physical NIC and virtio-user vdev
+ connected to a vhost-net socket.
+ * Configure the tap interface that is created with IP address and
+ set link state to UP.
+ * Launch second testpmd session with af_packet vdev connected to
+ the tap interface.
+ * Start packet forwarding on both testpmd sessions.
+ * Send 100 packets to the physical interface from external tester.
+ * Capture packets on the same physical interface.
+
+ Verify:
+ * Vhost session receives/forwards 100+ packets.
+ * Physical interface receives all 100 sent packets.
+ """
+ self.sut_node = self._ctx.sut_node
+ if not isinstance(self.sut_node.main_session, LinuxSession):
+ verify(False, "Must be running on a Linux environment.")
+ with TestPmd(
+ prefix="virtio",
+ vdevs=[VirtualDevice("virtio_user0,path=/dev/vhost-net,queues=1,queue_size=1024")],
+ ) as virtio:
+ self.sut_node.main_session.set_interface_link_up(name="tap0")
+ with TestPmd(
+ prefix="vhost", no_pci=True, vdevs=[VirtualDevice("net_af_packet0,iface=tap0")]
+ ) as vhost:
+ virtio.set_forward_mode(SimpleForwardingModes.mac)
+ vhost.set_forward_mode(SimpleForwardingModes.mac)
+ vhost.start()
+ virtio.start()
+
+ packet = Ether() / IP()
+ packets = [packet] * 100
+ captured_packets = send_packets_and_capture(packets)
+
+ vhost.stop()
+ virtio.stop()
+
+ vhost_forwarding_stats, vhost_raw_output = vhost.show_port_stats_all()
+
+ rx_packets = vhost_forwarding_stats[0].rx_packets
+ tx_packets = vhost_forwarding_stats[0].tx_packets
+
+ log(f"Vhost forwarding statistics:\n{vhost_raw_output}")
+
+ verify(
+ rx_packets >= 100 and tx_packets >= 100,
+ f"PVP loop forwarding verification failed: vhost interface RX={rx_packets},"
+ f" TX={tx_packets} (expected ≥100 each).",
+ )
+
+ verify(
+ len(captured_packets) >= 100, "Sent packets not received on physical interface."
+ )