From: Sanjeeva Yerrapureddy <[email protected]>

When a guest transmits a short Ethernet frame, iov_size() returns the
padded wire length including any bytes added to reach the Ethernet
minimum frame size of 60 bytes.  net_tx_pkt_rebuild_payload() uses
this inflated size as payload_len.  net_tx_pkt_update_ip_hdr_checksum()
then overwrites the IPv4 Total Length field with payload_len +
l3_hdr_len, inflating it by the padding.  The receiver interprets
Ethernet padding as IP payload, producing a malformed packet.

Fix by removing the ip_len write from net_tx_pkt_update_ip_hdr_checksum()
so it only recomputes the checksum, and moving the ip_len assignment
into net_tx_pkt_update_ip_checksums() where it is only performed for
TSO (where ip_len must be derived from payload_len since the guest sets
ip_len=0 per Intel 82574 datasheet §7.3.4 for super-packets the host
will segment).

Both e1000e and igb already call net_tx_pkt_update_ip_hdr_checksum()
from their IXSM paths, so both are corrected by this single common-
layer change.

Signed-off-by: Sanjeeva Yerrapureddy <[email protected]>
Reivewed-by: Akihiko Odaki <[email protected]>
Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Message-ID: <20260629-net-tx-pkt-ip-length-padding-v5-1-16760e302...@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <[email protected]>
(cherry picked from commit 9e41b5d22aeb942ad6ecc3174504ff645a10da6a)
Signed-off-by: Michael Tokarev <[email protected]>

diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
index 903238dca24..b134348fe80 100644
--- a/hw/net/net_tx_pkt.c
+++ b/hw/net/net_tx_pkt.c
@@ -93,9 +93,6 @@ void net_tx_pkt_update_ip_hdr_checksum(struct NetTxPkt *pkt)
     uint16_t csum;
     assert(pkt);
 
-    pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len +
-        pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
-
     pkt->l3_hdr.ip.ip_sum = 0;
     csum = net_raw_checksum(pkt->l3_hdr.octets,
         pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
@@ -117,7 +114,9 @@ void net_tx_pkt_update_ip_checksums(struct NetTxPkt *pkt)
 
     if (gso_type == VIRTIO_NET_HDR_GSO_TCPV4 ||
         gso_type == VIRTIO_NET_HDR_GSO_UDP) {
-        /* Calculate IP header checksum */
+        /* Set ip_len and calculate IP header checksum */
+        pkt->l3_hdr.ip.ip_len = cpu_to_be16(pkt->payload_len +
+            pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_len);
         net_tx_pkt_update_ip_hdr_checksum(pkt);
 
         /* Calculate IP pseudo header checksum */
-- 
2.47.3


Reply via email to