RP1 GEM TX checksum offload emits 0x0000 when an IPv4 UDP checksum calculates to zero
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- c, linux
- Domain
- embedded-iot, networking, operating-systems
Research direction
Start with the RP1/GEM checksum paths in drivers/net/ethernet/cadence/macb_main.c, especially the cited TX handling, and compare them with the zero-result handling in net/core/dev.c. Use the supplied Python reproducer with ethtool TX offload enabled and disabled, then verify that the external capture reports 0xffff rather than 0x0000 and that the software path remains valid.
Written by the indexing model from the issue text.
Description
Describe the bug
On a Raspberry Pi 5, the RP1 GEM TX checksum-offload path emitted an IPv4 UDP
checksum of 0x0000 when the calculated checksum was zero. Because the socket
had requested a UDP checksum, RFC 768 requires this result to be encoded as
0xffff; 0x0000 instead means that no checksum was generated.
The packet originated in a VM bridged through the Pi host. A capture in the VM
showed a CHECKSUM_PARTIAL-style seed, while a capture on a separate Internet
host showed 0x0000 on the wire. When checksum calculation was performed in
software, the same test produced 0xffff before NAT and a valid adjusted
checksum after NAT.
This matters even though a zero checksum is legal for IPv4 UDP receivers: it
silently removes the integrity check requested by the sender, and packet
transformations may treat it as an intentionally absent checksum. In the
original workload this caused one deterministic sequence gap and eventually a
reliable transport timeout.
Steps to reproduce the behaviour
First create the temporary reproducer below. It chooses a two-byte payload so
the IPv4 UDP checksum for the supplied source/destination tuple calculates to
zero, then sends that payload ten times:
cat >/tmp/udp-zero-checksum-repro.py <<'PY'
#!/usr/bin/env python3
import ipaddress
import socket
import struct
import sys
import time
def fold_sum(data):
if len(data) % 2:
data += b'\x00'
total = sum(struct.unpack(f'!{len(data) // 2}H', data))
while total >> 16:
total = (total & 0xffff) + (total >> 16)
return total
source, destination = sys.argv[1:3]
source_port, destination_port = map(int, sys.argv[3:5])
udp_length = 8 + 2
protocol = struct.pack('!BBH', 0, socket.IPPROTO_UDP, udp_length)
pseudo_header = (
ipaddress.IPv4Address(source).packed
+ ipaddress.IPv4Address(destination).packed
+ protocol
)
udp_header = struct.pack(
'!HHHH', source_port, destination_port, udp_length, 0
)
payload = struct.pack('!H', (~fold_sum(pseudo_header + udp_header)) & 0xffff)
assert fold_sum(pseudo_header + udp_header + payload) == 0xffff
print(f'payload={payload.hex()}')
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((source, source_port))
sock.connect((destination, destination_port))
for _ in range(10):
sock.send(payload)
time.sleep(0.1)
PY
-
On a separate receiver, start a UDP listener and capture on the receiving
interface. The capture must be taken on another machine because a sender
capture can show an unfinished checksum before hardware offload:nc -u -l 41000 >/dev/null sudo tcpdump -ni <receiver-interface> -vvv -XX 'udp port 41000' -
On the Pi 5, identify its source address and egress interface:
ip route get <receiver-ip> -
Enable TX checksum offload and send the crafted packets:
sudo ethtool -K <egress-interface> tx on python3 /tmp/udp-zero-checksum-repro.py \ <pi-source-ip> <receiver-ip> 43000 41000 -
Inspect the receiver capture. The suspected failure is a UDP checksum field
of0x0000(tcpdumpreportsno cksum). The expected field is0xffff
when there is no NAT, or a valid nonzero checksum if NAT changes an address
or port. -
Repeat with software checksumming:
sudo ethtool -K <egress-interface> tx off python3 /tmp/udp-zero-checksum-repro.py \ <pi-source-ip> <receiver-ip> 43001 41000Restore the original offload setting after the test.
In the original bridged-VM reproduction, the exact tuple and observed values
were:
| Guest TX checksum offload | Guest capture | External capture |
|---|---|---|
| enabled | partial seed 0xd26d |
0x0000 |
| disabled | valid 0xffff |
valid NAT-adjusted checksum 0x306b |
The enabled case used
192.168.0.36:43000 -> 51.15.222.118:41000 with payload e566. The external
capture contained:
... a7f8 a028 000a 0000 e566
^^^^ UDP checksum
Device (s)
Raspberry Pi 5
System
Raspberry Pi 5 Model B Rev 1.0
Kernel release: 6.12.93+rpt-rpi-2712
Architecture: aarch64
Network topology: VM connected through host bridge br0, then RP1 Ethernet
The matching Raspberry Pi archive source package is 6.12.93-1+rpt1.
Logs
No response
Additional context
Linux 6.12.93's macb driver advertises NETIF_F_HW_CSUM for GEM devices and
clears the checksum field for CHECKSUM_PARTIAL packets before hardware
completion:
- https://github.com/gregkh/linux/blob/v6.12.93/drivers/net/ethernet/cadence/macb_main.c#L2333-L2348
- https://github.com/gregkh/linux/blob/v6.12.93/drivers/net/ethernet/cadence/macb_main.c#L4302-L4314
The generic software fallback explicitly substitutes CSUM_MANGLED_0
(0xffff) when the calculated result is zero:
This suggests that RP1/GEM checksum completion returns raw zero, or that the
driver needs an RP1-specific workaround before advertising this offload. I have
not tested a kernel patch and am not yet proposing whether the appropriate fix
is a hardware setting, an RP1 quirk, or software UDP checksum completion.
There is an older report with the same zero-result failure class, but it
concerns different Raspberry Pi hardware, a different driver, and IPv6:
- Dominant language
- C
- Stars
- 13.2k
- Forks
- 5.5k
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 21
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from raspberrypi/linux
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
raspberrypi/linux#7415 · 2 comments · 1 reaction ·
-
rp1-cfe doesn't forward V4L2_EVENT_SOURCE_CHANGE event from csi-2 sensor driver to userspace app Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
raspberrypi/linux#7399 · 1 comment ·
-
Difficulty 1/5 Under an hour Newbie friendliness 82/100
raspberrypi/linux#7357 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
raspberrypi/linux#7054 · 2 comments ·
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
raspberrypi/linux#7634 · 8 comments · 1 reaction ·
All issues in raspberrypi/linux
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
zephyrproject-rtos/zephyr#119726 ·
-
[Bounty proposal] fix(web): memory insights count an evening memory on the next day ($25 proposed) Open
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
BasedHardware/omi#15320 ·
-
[adam] AdamNet network read doesn't cap to MAX_ADAM_PACKET_LEN, overflows client receive buffers Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
FujiNetWIFI/fujinet-firmware#1649 · 2 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
HarbourMasters/Shipwright#7229 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
riscv-software-src/riscv-isa-sim#2435 · 1 comment ·