From: David Gibson <david@gibson.dropbear.id.au>
To: passt-dev@passt.top, Stefano Brivio <sbrivio@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>,
David Gibson <dgibson@redhat.com>
Subject: [PATCH 6/9] tap: Use explicit defines for maximum length of L2 frame
Date: Tue, 11 Mar 2025 17:03:15 +1100 [thread overview]
Message-ID: <20250311060318.1502861-7-david@gibson.dropbear.id.au> (raw)
In-Reply-To: <20250311060318.1502861-1-david@gibson.dropbear.id.au>
Currently in tap.c we (mostly) use ETH_MAX_MTU as the maximum length of
an L2 frame. This define comes from the kernel, but it's badly named and
used confusingly.
First, it doesn't really have anything to do with Ethernet, which has no
structural limit on frame lengths. It comes more from either a) IP which
imposes a 64k datagram limit or b) from internal buffers used in various
places in the kernel (and in passt).
Worse, MTU generally means the maximum size of the IP (L3) datagram which
may be transferred, _not_ counting the L2 headers. In the kernel
ETH_MAX_MTU is sometimes used that way, but sometimes seems to be used as
a maximum frame length, _including_ L2 headers. In tap.c we're mostly
using it in the second way.
Finally, each of our tap backends could have different limits on the frame
size imposed by the mechanisms they're using.
Start clearing up this confusion by replacing it in tap.c with new
L2_MAX_LEN_* defines which specifically refer to the maximum L2 frame
length for each backend.
Signed-off-by: David Gibson <dgibson@redhat.com>
---
tap.c | 18 ++++++++++++++----
tap.h | 25 +++++++++++++++++++++++++
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/tap.c b/tap.c
index fb306e75..4840dcfa 100644
--- a/tap.c
+++ b/tap.c
@@ -62,6 +62,15 @@
#include "vhost_user.h"
#include "vu_common.h"
+/* Maximum allowed frame lengths (including L2 header) */
+
+static_assert(L2_MAX_LEN_PASTA <= PACKET_MAX_LEN,
+ "packet pool can't store maximum size pasta frame");
+static_assert(L2_MAX_LEN_PASST <= PACKET_MAX_LEN,
+ "packet pool can't store maximum size qemu socket frame");
+static_assert(L2_MAX_LEN_VU <= PACKET_MAX_LEN,
+ "packet pool can't store maximum size vhost-user frame");
+
/* IPv4 (plus ARP) and IPv6 message batches from tap/guest to IP handlers */
static PACKET_POOL_NOINIT(pool_tap4, TAP_MSGS, pkt_buf);
static PACKET_POOL_NOINIT(pool_tap6, TAP_MSGS, pkt_buf);
@@ -1097,7 +1106,8 @@ static void tap_passt_input(struct ctx *c, const struct timespec *now)
while (n >= (ssize_t)sizeof(uint32_t)) {
uint32_t l2len = ntohl_unaligned(p);
- if (l2len < sizeof(struct ethhdr) || l2len > ETH_MAX_MTU) {
+ if (l2len < sizeof(struct ethhdr) ||
+ l2len > L2_MAX_LEN_PASST) {
err("Bad frame size from guest, resetting connection");
tap_sock_reset(c);
return;
@@ -1151,8 +1161,8 @@ static void tap_pasta_input(struct ctx *c, const struct timespec *now)
tap_flush_pools();
- for (n = 0; n <= (ssize_t)(sizeof(pkt_buf) - ETH_MAX_MTU); n += len) {
- len = read(c->fd_tap, pkt_buf + n, ETH_MAX_MTU);
+ for (n = 0; n <= (ssize_t)(sizeof(pkt_buf) - L2_MAX_LEN_PASTA); n += len) {
+ len = read(c->fd_tap, pkt_buf + n, L2_MAX_LEN_PASTA);
if (len == 0) {
die("EOF on tap device, exiting");
@@ -1170,7 +1180,7 @@ static void tap_pasta_input(struct ctx *c, const struct timespec *now)
/* Ignore frames of bad length */
if (len < (ssize_t)sizeof(struct ethhdr) ||
- len > (ssize_t)ETH_MAX_MTU)
+ len > (ssize_t)L2_MAX_LEN_PASTA)
continue;
tap_add_packet(c, len, pkt_buf + n);
diff --git a/tap.h b/tap.h
index a2c3b87d..140e3305 100644
--- a/tap.h
+++ b/tap.h
@@ -6,6 +6,31 @@
#ifndef TAP_H
#define TAP_H
+/** L2_MAX_LEN_PASTA - Maximum frame length for pasta mode (with L2 header)
+ *
+ * The kernel tuntap device imposes a maximum frame size of 65535 including
+ * 'hard_header_len' (14 bytes for L2 Ethernet in the case of "tap" mode).
+ */
+#define L2_MAX_LEN_PASTA USHRT_MAX
+
+/** L2_MAX_LEN_PASST - Maximum frame length for passt mode (with L2 header)
+ *
+ * The only structural limit the Qemu socket protocol imposes on frames is
+ * (2^32-1) bytes, but that would be ludicrously long in practice. For now,
+ * limit it somewhat arbitrarily to 65535 bytes. FIXME: Work out an appropriate
+ * limit with more precision.
+ */
+#define L2_MAX_LEN_PASST USHRT_MAX
+
+/** L2_MAX_LEN_VU - Maximum frame length for vhost-user mode (with L2 header)
+ *
+ * VU allows multiple buffers per frame, each of which can be quite large, so
+ * the inherent frame size limit is rather large. Much larger than is actually
+ * useful for IP. For now limit arbitrarily to 65535 bytes. FIXME: Work out an
+ * appropriate limit with more precision.
+ */
+#define L2_MAX_LEN_VU USHRT_MAX
+
struct udphdr;
/**
--
@@ -6,6 +6,31 @@
#ifndef TAP_H
#define TAP_H
+/** L2_MAX_LEN_PASTA - Maximum frame length for pasta mode (with L2 header)
+ *
+ * The kernel tuntap device imposes a maximum frame size of 65535 including
+ * 'hard_header_len' (14 bytes for L2 Ethernet in the case of "tap" mode).
+ */
+#define L2_MAX_LEN_PASTA USHRT_MAX
+
+/** L2_MAX_LEN_PASST - Maximum frame length for passt mode (with L2 header)
+ *
+ * The only structural limit the Qemu socket protocol imposes on frames is
+ * (2^32-1) bytes, but that would be ludicrously long in practice. For now,
+ * limit it somewhat arbitrarily to 65535 bytes. FIXME: Work out an appropriate
+ * limit with more precision.
+ */
+#define L2_MAX_LEN_PASST USHRT_MAX
+
+/** L2_MAX_LEN_VU - Maximum frame length for vhost-user mode (with L2 header)
+ *
+ * VU allows multiple buffers per frame, each of which can be quite large, so
+ * the inherent frame size limit is rather large. Much larger than is actually
+ * useful for IP. For now limit arbitrarily to 65535 bytes. FIXME: Work out an
+ * appropriate limit with more precision.
+ */
+#define L2_MAX_LEN_VU USHRT_MAX
+
struct udphdr;
/**
--
2.48.1
next prev parent reply other threads:[~2025-03-11 6:03 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-11 6:03 [PATCH 0/9] Improve handling of MTU limits David Gibson
2025-03-11 6:03 ` [PATCH 1/9] conf: Use the same optstring for passt and pasta modes David Gibson
2025-03-11 6:03 ` [PATCH 2/9] conf: Move mode detection into helper function David Gibson
2025-03-11 6:03 ` [PATCH 3/9] conf: Detect vhost-user mode earlier David Gibson
2025-03-11 22:45 ` Stefano Brivio
2025-03-12 0:48 ` David Gibson
2025-03-11 6:03 ` [PATCH 4/9] packet: Give explicit name to maximum packet size David Gibson
2025-03-11 6:03 ` [PATCH 5/9] packet: Remove redundant TAP_BUF_BYTES define David Gibson
2025-03-11 6:03 ` David Gibson [this message]
2025-03-11 22:45 ` [PATCH 6/9] tap: Use explicit defines for maximum length of L2 frame Stefano Brivio
2025-03-12 0:56 ` David Gibson
2025-03-11 6:03 ` [PATCH 7/9] Simplify sizing of pkt_buf David Gibson
2025-03-11 6:03 ` [PATCH 8/9] pcap: Correctly set snaplen based on tap backend type David Gibson
2025-03-11 6:03 ` [PATCH 9/9] conf: Limit maximum MTU based on backend frame size David Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250311060318.1502861-7-david@gibson.dropbear.id.au \
--to=david@gibson.dropbear.id.au \
--cc=dgibson@redhat.com \
--cc=passt-dev@passt.top \
--cc=sbrivio@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://passt.top/passt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).