From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: passt.top; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=H7Th3/hU; dkim-atps=neutral Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by passt.top (Postfix) with ESMTPS id A32355A0269 for ; Tue, 16 Jun 2026 18:37:18 +0200 (CEST) Received: by mail-wr1-x42e.google.com with SMTP id ffacd0b85a97d-45ef1198766so38863f8f.0 for ; Tue, 16 Jun 2026 09:37:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781627838; x=1782232638; darn=passt.top; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=GOaFnSOrXAgka2XiuBaKqcDjYRGvJk/qoWUzJDNQma0=; b=H7Th3/hU1nXN6BvUl0DdFgpK3KxyZ0XlYmJTThc5VdV8KZSvVOqDM6Vue0wxS2c6S0 uLt4g1ECoJJvuN1mLvgaLe8JYaOnMlSCcArg3TT1Ie9hf/IGDUBV6ybtB/xAV8TaXElH fgLTYGeUh+gwHquO9CO+W+DMOd3Y03VpZW9HOFj0Du2tIUYq2QCSyqIdUgb9Mdp/yTUz 1jX3NEleiFXRF9O0gXWbHclwaNN4AtLFspam5ghOIdx2+WKFxzNL4Z7InZ700Wk313Hu altE4vXOF5yDrdV0Imr1OwqMJ+jQDaOb8Q7FYdPNZrQQeIEdHddqxvmN/Jwsjs08PMxO QKbg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781627838; x=1782232638; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=GOaFnSOrXAgka2XiuBaKqcDjYRGvJk/qoWUzJDNQma0=; b=KHwc2/73TS+I9hJv3g6GGtDmOGeoZW7wzsJmh7LlYM6F7CZrAxa8b+s2GbD38rX/4R 28LnxXeHH8J9TczNjhEkNw2yQ51VkhgIFx7XSRtCYrTscjdVlQFYdJRJT8VuF7ig/PuX NVuLRF2QSjVZLpj6ymIx7l6XvkSYTzBG+Wvkk9wP2TDZepw0zswqJngzbsCvNFta9zO5 fHUV7wt5jeqa275ULBbZy5FxeprjobY1zlVQUi9yBvkW7joG77/bk4rOk5dA+D0FkBUt 4n80+Ov1XGXfiT4249vE+PON6rOmXynT+c8wfKKaIUnls+vf46MLMAlHFwEQqPjkJOCQ /GZQ== X-Gm-Message-State: AOJu0YwBz8Xrikv5+5siNdM9LMulTNRUna/XV9I99vQ6Wd+wDA/OPavj d2snmW+uyg3GkI+pLabX2SFbA+QANF9T0kLXpIegQtHkjF38VRhfu/gduUktRUVJ X-Gm-Gg: Acq92OE4YQhxXMNj2dvCh/RBbqc1P7sxWwjr66vVM2lB7kzn/nQmggmPHqRtLOuoiIV VvENxuL6bwLZFDXvYmx5P3x5uTVYcUd0SKhCKqdnl5+GNchp0pNeZzlEfeNr55h82sDCAG2hDKj c83WW4f76aZOdrJg6La5/lQhy1tLWtqm3wU+tx8EJsUeHHxORNuvwMbpBMbIEwj2fl1nEL/H1XO YhXvIU35Aw2csVAfw5CWuuG48yW56zi7m5CXSTqoxlEMdt9534SsrY0KFaMTYJfZVX5Sjg2mZLo peV2T+brZwUS9mrCnP1IDF5Zc8EZki+y3KjW1uO8wgnoCFAndZUSOuvkWHIQJmjU7eUEespsnk+ ivCh9fayy0Wa7mvduveKVk0Xgbcz8C/hXXSV1qSqgciAFt4v/lMFw1xJ21sbBHc6+khh9pv56va Eut4ewdqfqx9wDvE6GvyPIgY9Vib+3KnPV67wmtqLsIQoF+Sc5dlUUyny8bQ70RdsDVYFqpcG2y gGCrka0 X-Received: by 2002:a5d:6d4b:0:b0:45e:f93a:2069 with SMTP id ffacd0b85a97d-4619f31369fmr5835237f8f.23.1781627837901; Tue, 16 Jun 2026 09:37:17 -0700 (PDT) Received: from iron.home (2a01cb04046e8b00dabbc1fffe452271.ipv6.abo.wanadoo.fr. [2a01:cb04:46e:8b00:dabb:c1ff:fe45:2271]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4606f2dbfb1sm48927375f8f.35.2026.06.16.09.37.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 16 Jun 2026 09:37:17 -0700 (PDT) From: David du Colombier <0intro@gmail.com> To: passt-dev@passt.top Subject: [PATCH v2] tap: Trim Ethernet padding from short IPv4 frames instead of dropping them Date: Tue, 16 Jun 2026 18:37:05 +0200 Message-ID: <20260616163705.1285803-1-0intro@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260612214338.12345-1-0intro@gmail.com> References: <20260612214338.12345-1-0intro@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-MailFrom: 0intro@gmail.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation Message-ID-Hash: FUP2IRLNGSSROFZ6HAWAYMQVK3TPVXGH X-Message-ID-Hash: FUP2IRLNGSSROFZ6HAWAYMQVK3TPVXGH X-Mailman-Approved-At: Tue, 16 Jun 2026 23:00:45 +0200 CC: David du Colombier <0intro@gmail.com> X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: tap4_handler() requires the L2 payload after the IP header to match the IP datagram length exactly. Guests whose drivers pad transmitted frames to the 60 byte Ethernet minimum, as real hardware requires and as drivers modelled on hardware do (Plan 9's virtio-net, for one), send pure ACK and FIN segments as 60 byte frames: 14 byte Ethernet header, 40 byte IPv4 datagram, 6 padding octets. Those frames fail the exact length check and are dropped without trace. passt then never sees such a guest's acknowledgements: it retransmits from the lowest unacknowledged sequence with exponential backoff while the guest, which received and acknowledged everything, waits. Every fresh connection stalls for minutes (a 1 MiB HTTP fetch over --map-host-loopback measured 248 s before this change, 0.27 s after; bulk transfer over established connections, whose ACKs ride data segments above the padding threshold, is unaffected). FIN segments are padded too, so teardown hangs as well. Note that tap_send_single() pads passt's own outbound frames to ETH_ZLEN, so the receive path was already stricter than the send path. Trim the trailing padding to the IP datagram length instead, using a new iov_tail_trim() helper, and keep dropping frames genuinely shorter than the datagram they claim to carry. IPv6 is unaffected: its minimal TCP frame is 74 bytes, above the padding threshold. Signed-off-by: David du Colombier <0intro@gmail.com> --- v2: - store the iov_tail_size() result in a local variable instead of computing it twice - pass ARRAY_SIZE(trim_iov) instead of UIO_MAXIOV iov.c | 35 +++++++++++++++++++++++++++++++++++ iov.h | 2 ++ tap.c | 15 +++++++++++++-- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/iov.c b/iov.c index 6fd684a..968a365 100644 --- a/iov.c +++ b/iov.c @@ -450,3 +450,38 @@ ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, return j; } + +/** + * iov_tail_trim() - Limit a tail to @len bytes via a scratch iovec array + * @tail: Pointer to the iov_tail to trim; rebuilt on success to + * reference @scratch + * @len: Number of bytes to keep + * @scratch: Scratch iovec array backing the trimmed tail; must stay + * valid as long as the trimmed tail is in use + * @scratch_cnt: Number of elements in @scratch + * + * Return: true on success, false if @tail is shorter than @len or does + * not fit in @scratch (@tail is unchanged on failure) + */ +bool iov_tail_trim(struct iov_tail *tail, size_t len, + struct iovec *scratch, size_t scratch_cnt) +{ + ssize_t cnt = iov_tail_clone(scratch, scratch_cnt, tail); + size_t left = len; + unsigned int i; + + if (cnt < 0) + return false; + + for (i = 0; i < (size_t)cnt && left; i++) { + if (scratch[i].iov_len > left) + scratch[i].iov_len = left; + left -= scratch[i].iov_len; + } + + if (left) + return false; + + *tail = IOV_TAIL(scratch, i, 0); + return true; +} diff --git a/iov.h b/iov.h index 4fdf14a..3af467e 100644 --- a/iov.h +++ b/iov.h @@ -97,6 +97,8 @@ size_t iov_push_header_(struct iov_tail *tail, const void *v, size_t len); void *iov_remove_header_(struct iov_tail *tail, void *v, size_t len, size_t align); ssize_t iov_tail_clone(struct iovec *dst_iov, size_t dst_iov_cnt, struct iov_tail *tail); +bool iov_tail_trim(struct iov_tail *tail, size_t len, + struct iovec *scratch, size_t scratch_cnt); /** * IOV_PEEK_HEADER() - Get typed pointer to a header from an IOV tail diff --git a/tap.c b/tap.c index 4cba4c7..6ed7f8d 100644 --- a/tap.c +++ b/tap.c @@ -716,7 +716,8 @@ static int tap4_handler(struct ctx *c, const struct pool *in, i = 0; resume: for (seq_count = 0, seq = NULL; i < in->count; i++) { - size_t l3len, hlen, l4len; + struct iovec trim_iov[UIO_MAXIOV]; + size_t l3len, hlen, l4len, check; struct ethhdr eh_storage; struct iphdr iph_storage; struct udphdr uh_storage; @@ -775,7 +776,17 @@ resume: if (!iov_drop_header(&data, hlen)) continue; - if (iov_tail_size(&data) != l4len) + + check = iov_tail_size(&data); + if (check < l4len) + continue; + + /* Drivers modelled on real hardware (Plan 9's virtio, for + * one) pad short frames to the 60 byte Ethernet minimum: + * trim trailing padding instead of dropping the packet. + */ + if (check > l4len && + !iov_tail_trim(&data, l4len, trim_iov, ARRAY_SIZE(trim_iov))) continue; if (iph->protocol == IPPROTO_ICMP) { -- 2.54.0