RFC 768 requires that when the UDP payload has an odd length, the last
byte is padded with a zero low byte for the checksum calculation, i.e. it
must be added as (byte << 8) only.

Since commit 8524423da9af ("net: avoid address-of-packed-member error")
the byte is added twice:

    xsum += (sumptr[0] << 8) + sumptr[0];

That commit replaced the correctly masked

    sumdata = *(unsigned char *)sumptr;
    sumdata = (sumdata << 8) & 0xff00;
    xsum += sumdata;

with the expression above, so with CONFIG_UDP_CHECKSUM enabled the
resulting sum for a valid packet is 0xffff + last_byte instead of
0xffff, and every received UDP packet with an odd length whose last byte
is non-zero is wrongly rejected with

    UDP wrong checksum 000000bf 00009288

and dropped. This breaks e.g. TFTP download of any file with an odd
size whose final byte is not zero, because the last data block of such
a file has an odd UDP length and is rejected on every retransmission.

Fix the calculation to add the byte as the high half only.

Fixes: 8524423da9af ("net: avoid address-of-packed-member error")
Signed-off-by: Mingtian Li <[email protected]>
Cc: Jerome Forissier <[email protected]>
---
Verification: replaying the receive path's checksum loop (the same
expressions as in net/net.c) over a synthetic TFTP DATA packet that
carries a correct checksum:

  odd-length datagram, udp_len 25, last byte 0xbf:
    current code -> xsum 0x000000bf, rejected with
                    " UDP wrong checksum 000000bf 00000958"
    patched code -> xsum 0x0000ffff, accepted

  even-length datagram, udp_len 24:
    current code -> xsum 0x0000ffff, accepted
    patched code -> xsum 0x0000ffff, accepted

So only the odd-length case changes, and it changes from wrongly
rejected to accepted.
---
 net/net.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/net.c b/net/net.c
index 71666eb..43889bb 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1408,8 +1408,9 @@ void net_process_received_packet(uchar *in_packet, int 
len)
                                sumptr += 2;
                                sumlen -= 2;
                        }
+                       /* last byte is padded with a zero byte (RFC 768) */
                        if (sumlen > 0)
-                               xsum += (sumptr[0] << 8) + sumptr[0];
+                               xsum += sumptr[0] << 8;
                        while ((xsum >> 16) != 0) {
                                xsum = (xsum & 0x0000ffff) +
                                       ((xsum >> 16) & 0x0000ffff);

---
base-commit: 211de43d0f954a00a490220c1aac9db298287c40
change-id: 20260920-udp-checksum-odd-43cbd1352bb8

Best regards,
--  
Mingtian Li <[email protected]>

Reply via email to