[PATCH 0/2] Fix minimum frame size checks in vhost-user paths
In the vhost-user code paths, buffers from the virtio queue include a virtio net header prepended to the Ethernet frame. The minimum frame size checks (ETH_ZLEN, i.e. 60 bytes per IEEE 802.3) must account for this extra header, otherwise the size requested from vu_collect() and validated in ASSERT() is too small. This two-patch series: 1. Introduce a VNET_HLEN macro in virtio.h to replace all open-coded sizeof(struct virtio_net_hdr_mrg_rxbuf) throughout the vhost-user code, improving readability and consistency. 2. Fix the minimum frame size calculations in tcp_vu.c and udp_vu.c to use ETH_ZLEN + VNET_HLEN instead of bare ETH_ZLEN, correctly accounting for the virtio net header in the buffer size. Laurent Vivier (2): virtio: Introduce VNET_HLEN macro for virtio net header length tcp_vu, udp_vu: Account for virtio net header in minimum frame size tcp_vu.c | 29 +++++++++++------------------ udp_vu.c | 14 +++++--------- virtio.h | 2 ++ vu_common.c | 13 +++++-------- vu_common.h | 2 +- 5 files changed, 24 insertions(+), 36 deletions(-) -- 2.52.0
Replace all open-coded sizeof(struct virtio_net_hdr_mrg_rxbuf) with a
VNET_HLEN macro.
Signed-off-by: Laurent Vivier
In the vhost-user paths, the buffers provided by the virtio queue
include the virtio net header (VNET_HLEN) prepended to the Ethernet
frame. The minimum size checks using ETH_ZLEN must therefore account
for this additional header length, otherwise we underestimate the
minimum buffer size needed.
Use ETH_ZLEN + VNET_HLEN instead of bare ETH_ZLEN in vu_collect()
calls and the corresponding ASSERT() checks.
Fixes: 0cb8f9003654 ("tcp, udp: Pad batched frames for vhost-user modes to 60 bytes (802.3 minimum)")
Signed-off-by: Laurent Vivier
On Thu, 12 Feb 2026 12:39:31 +0100
Laurent Vivier
Replace all open-coded sizeof(struct virtio_net_hdr_mrg_rxbuf) with a VNET_HLEN macro.
Signed-off-by: Laurent Vivier
--- tcp_vu.c | 21 +++++++-------------- udp_vu.c | 12 ++++-------- virtio.h | 2 ++ vu_common.c | 13 +++++-------- vu_common.h | 2 +- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/tcp_vu.c b/tcp_vu.c index b9e9b55ed3d3..f7bda4943e43 100644 --- a/tcp_vu.c +++ b/tcp_vu.c @@ -49,8 +49,7 @@ static size_t tcp_vu_hdrlen(bool v6) { size_t hdrlen;
- hdrlen = sizeof(struct virtio_net_hdr_mrg_rxbuf) + - sizeof(struct ethhdr) + sizeof(struct tcphdr); + hdrlen = VNET_HLEN + sizeof(struct ethhdr) + sizeof(struct tcphdr);
My eyes thank you dearly. -- Stefano
On Thu, 12 Feb 2026 12:39:32 +0100
Laurent Vivier
In the vhost-user paths, the buffers provided by the virtio queue include the virtio net header (VNET_HLEN) prepended to the Ethernet frame. The minimum size checks using ETH_ZLEN must therefore account for this additional header length, otherwise we underestimate the minimum buffer size needed.
Oops, thanks for fixing this.
Use ETH_ZLEN + VNET_HLEN instead of bare ETH_ZLEN in vu_collect() calls and the corresponding ASSERT() checks.
Fixes: 0cb8f9003654 ("tcp, udp: Pad batched frames for vhost-user modes to 60 bytes (802.3 minimum)") Signed-off-by: Laurent Vivier
--- tcp_vu.c | 8 ++++---- udp_vu.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tcp_vu.c b/tcp_vu.c index f7bda4943e43..2d593d534d68 100644 --- a/tcp_vu.c +++ b/tcp_vu.c @@ -90,12 +90,12 @@ int tcp_vu_send_flag(const struct ctx *c, struct tcp_tap_conn *conn, int flags) vu_set_element(&flags_elem[0], NULL, &flags_iov[0]);
elem_cnt = vu_collect(vdev, vq, &flags_elem[0], 1, - MAX(hdrlen + sizeof(*opts), ETH_ZLEN), NULL); + MAX(hdrlen + sizeof(*opts), ETH_ZLEN + VNET_HLEN), NULL);
I'm applying this now but, perhaps as a follow-up, should we consider to clarify further the arguments to vu_collect(), and perhaps related sizes? I'm not the most indicated person to do this I guess, as I keep getting them wrong. :( In this case, I really thought VNET_HLEN would be added internally, because of: * @size: Maximum size of the data in the frame -- Stefano
On Thu, 12 Feb 2026 12:39:32 +0100
Laurent Vivier
In the vhost-user paths, the buffers provided by the virtio queue include the virtio net header (VNET_HLEN) prepended to the Ethernet frame. The minimum size checks using ETH_ZLEN must therefore account for this additional header length, otherwise we underestimate the minimum buffer size needed.
Use ETH_ZLEN + VNET_HLEN instead of bare ETH_ZLEN in vu_collect() calls and the corresponding ASSERT() checks.
Fixes: 0cb8f9003654 ("tcp, udp: Pad batched frames for vhost-user modes to 60 bytes (802.3 minimum)") Signed-off-by: Laurent Vivier
--- tcp_vu.c | 8 ++++---- udp_vu.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tcp_vu.c b/tcp_vu.c index f7bda4943e43..2d593d534d68 100644 --- a/tcp_vu.c +++ b/tcp_vu.c @@ -90,12 +90,12 @@ int tcp_vu_send_flag(const struct ctx *c, struct tcp_tap_conn *conn, int flags) vu_set_element(&flags_elem[0], NULL, &flags_iov[0]);
elem_cnt = vu_collect(vdev, vq, &flags_elem[0], 1, - MAX(hdrlen + sizeof(*opts), ETH_ZLEN), NULL); + MAX(hdrlen + sizeof(*opts), ETH_ZLEN + VNET_HLEN), NULL); if (elem_cnt != 1) return -1;
ASSERT(flags_elem[0].in_sg[0].iov_len >= - MAX(hdrlen + sizeof(*opts), ETH_ZLEN)); + MAX(hdrlen + sizeof(*opts), ETH_ZLEN + VNET_HLEN));
vu_set_vnethdr(vdev, flags_elem[0].in_sg[0].iov_base, 1);
@@ -208,7 +208,7 @@ static ssize_t tcp_vu_sock_recv(const struct ctx *c, struct vu_virtq *vq,
cnt = vu_collect(vdev, vq, &elem[elem_cnt], VIRTQUEUE_MAX_SIZE - elem_cnt, - MAX(MIN(mss, fillsize) + hdrlen, ETH_ZLEN), + MAX(MIN(mss, fillsize) + hdrlen, ETH_ZLEN + VNET_HLEN), &frame_size); if (cnt == 0) break; @@ -302,7 +302,7 @@ static void tcp_vu_prepare(const struct ctx *c, struct tcp_tap_conn *conn, /* we guess the first iovec provided by the guest can embed * all the headers needed by L2 frame, including any padding */ - ASSERT(iov[0].iov_len >= MAX(hdrlen, ETH_ZLEN)); + ASSERT(iov[0].iov_len >= MAX(hdrlen, ETH_ZLEN + VNET_HLEN));
This triggers in the passt_vu_in_ns/tcp, "TCP/IPv4: host to guest: big transfer" test case, that is, the first time we connect to the guest (probably on the initial SYN segment). I didn't check why. I applied 1/2, but not this one. -- Stefano
participants (2)
-
Laurent Vivier
-
Stefano Brivio