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=o4+Tu6tf; dkim-atps=neutral Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by passt.top (Postfix) with ESMTPS id 036415A0271 for ; Fri, 12 Jun 2026 23:58:11 +0200 (CEST) Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-490aebf33e9so6829085e9.3 for ; Fri, 12 Jun 2026 14:58:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781301490; x=1781906290; darn=passt.top; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=iQHbvSL/5nSs7ir2tD2s5RcjdpPMNAu9o3QZMNPl3/Q=; b=o4+Tu6tf3HhUMvVyhbm0xjykq2Qvq+Lq9S3w50K44lpJQE/TbbJtecvYyyr4ePsUEu Az5T/s0vrFdQtH0ekWNXhx5GKmdtup+gvG5LuAENG5YK8PbbYdgZThn4Ube2W69YmLye Dnz6G+5h+8DHELLvC9wza/ejXxjjNsECoZp3bMdSNBHwSx8vMrxSMkgic+9ZcegoDVCm 9u9QocI39so523J8eXmZLjr+eoacQcrO7l8JEqhRYZLbznVANwDpwsLrq3dZoKwDJN9Z MOKSjvAxUxRY0jeEdiZzULfkgEsGgEUnyIqJZ/4RngRrG14gOMEedMuTnkUeMTEfLEMj N/xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781301490; x=1781906290; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=iQHbvSL/5nSs7ir2tD2s5RcjdpPMNAu9o3QZMNPl3/Q=; b=qssgBSegX6dtIC7oOE8VQE2sLgxQsnSAofjAwcpeA68YV8q35P48DHNLFaB1tFNyir +zMklS0BwB31mX42cZAPcnF3rIAjDmrT72XZLa9nuu9dPpysMyjuH5IVXHUhvrkBs2FT fmywdcBLhM6gXQLbJFt0XYj/djyLe7orhNzOdwJ8tISQP7ppDbKbMl6BRXvnHX+Fka9c mXkAg1THHk9hCzBSZ9Ws7APQrx7w6B+FVZ+oOm5OFeRL1gjtKpfrscQ0ejKjVRXQqJ6U NzjDIWUSTbAVQDOgHTtU1drQaaWRoloHRwD1EJSKIAOs44MH9MM/basVKK0GC4MVzr+S hPgw== X-Gm-Message-State: AOJu0Yy8Pkjx7SLhTgTtzr0IvmE2oe3+guYAm8iHS8U4LTpo382gWD1T eNzTwiICLtEveBKhAldWhTzhpq9QOkZ3pSXQZth2txfBZRtOCMrKCI/kml1uFczG X-Gm-Gg: Acq92OFxYxA8m2kBtDO9O1y3ZqVNsv6CCgcAd7wEDv1or2UVRTmpEFCv/MVqMoU4eO7 zF06U1vGvu+hGj12KCKQSHn2h2jDqw6pgrkUZqLhfwvnPOKFJymLd/jDCXM9vvwdG4uqvkSH2xK UXJ33p6TJJh2wwekvM5EnICwfGhpoW4fgTLLmPhLV5c9BtAdTG7+aFguKiBPNtwnJnQLWS2xXBK pSU7ufDEqlnmZVGolLB4ZBfi82Yx7tkRWJ19ZF+snmIh5CPS0xQAUvghCV6+b2XThb+MLQ5bKoi lT6V51h5HIz/TGSNKR03PSJbjW04i+7YxPNE6tpxK4P+RpBLxnDPB3A0lsfwlkl8oC4Ye1I2lAA FKQEbq2F5Yg7rEN/Sq0fjlV+g902ltLlpLLjjCASfiKhfsOOueJ5Ova5NOfFD7jrjrT4TrjqupS RJCFYhKXgyINSOTKquoSRXFiU8BP42v+AJJ/g2e2UXCA7jAHE3JeOS2FVJbTmzMrq9dW5zVfeDQ KjWGMHS X-Received: by 2002:a05:600c:214e:b0:490:e180:2ed with SMTP id 5b1f17b1804b1-490ec4bfe4cmr36688575e9.4.1781301490345; Fri, 12 Jun 2026 14:58:10 -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 5b1f17b1804b1-49220310a58sm22174295e9.4.2026.06.12.14.58.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 12 Jun 2026 14:58:09 -0700 (PDT) From: David du Colombier <0intro@gmail.com> To: passt-dev@passt.top Subject: [PATCH] tap: Trim Ethernet padding from short IPv4 frames instead of dropping them Date: Fri, 12 Jun 2026 23:58:04 +0200 Message-ID: <20260612215804.710266-1-0intro@gmail.com> X-Mailer: git-send-email 2.54.0 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: FCJKZCM7LN5NNIU5RNJXCURHX6LSK6GN X-Message-ID-Hash: FCJKZCM7LN5NNIU5RNJXCURHX6LSK6GN X-Mailman-Approved-At: Sat, 13 Jun 2026 00:45:39 +0200 CC: sbrivio@redhat.com, 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> --- iov.c | 35 +++++++++++++++++++++++++++++++++++ iov.h | 2 ++ tap.c | 11 ++++++++++- 3 files changed, 47 insertions(+), 1 deletion(-) 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..b929b21 100644 --- a/tap.c +++ b/tap.c @@ -716,6 +716,7 @@ static int tap4_handler(struct ctx *c, const struct pool *in, i = 0; resume: for (seq_count = 0, seq = NULL; i < in->count; i++) { + struct iovec trim_iov[UIO_MAXIOV]; size_t l3len, hlen, l4len; struct ethhdr eh_storage; struct iphdr iph_storage; @@ -775,7 +776,15 @@ resume: if (!iov_drop_header(&data, hlen)) continue; - if (iov_tail_size(&data) != l4len) + if (iov_tail_size(&data) < 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 (iov_tail_size(&data) > l4len && + !iov_tail_trim(&data, l4len, trim_iov, UIO_MAXIOV)) continue; if (iph->protocol == IPPROTO_ICMP) { -- 2.54.0