We've noticed that some % of upstream packets going to from our office wifi to Cloudflare are partially replaced by all 0-bytes. This happens above the network layer because the TCP checksum is correct.
We typically observe this as https uploads failing with "bad record mac", but we can reproduce the exact corruption pattern using a public echo server.
We are not the only people with this problem: Xfinity forum, March 2026 · Xfinity forum, July 2026 · Efpophis gist
Interestingly, the other Comcast customer in the same office as us does not have the problem (despite their router being in the same room as ours); and other upstream destinations (i.e. not Cloudflare) are not affected from our network.
What we observe through the echo-server is that the tail end of some IP packets are being replaced by all zeros:
- the first 230 bytes of the IP packet survive intact -- constant across IPv4 and IPv6 once the 20-byte header size difference is accounted for
- every byte after that arrives as
0x00 - the zeroed run ends precisely on a TCP segment boundary
- total stream length is preserved and adjacent segments are untouched
- the damaged packet arrives with a valid TCP checksum, so the receiving stack accepts it and the byte stream stays aligned
Measured payload sizes: 1198 bytes zeroed over IPv4, 1178 over IPv6.
Adding headers back gives the same figure both ways:
20+20+1198 = 40+20+1178 = 1238.
Verified on both address families, each measured against the MSS of its own connection:
| Family | MSS | Zeroed | IP+TCP headers | Packet bytes kept |
|---|---|---|---|---|
| IPv4 | 1388 | 1198 | 40 | 230 |
| IPv6 | 1348 | 1178 | 60 | 230 |
Observed rate: roughly 1 damaged segment per 20-50 MB uploaded. Failure probability scales with bytes sent after about the first 50 KB of a connection -- 64 KiB uploads fail ~0.1% of the time, 1 MiB uploads 7-12%.
Affected circuit, public IP 73.243.42.115, Cloudflare colo DEN, over Wi-Fi:
./upload-test.sh https://cloudflare.com/ 150 -> 5/150 failed
./upload-test.sh https://s3.us-west-2.amazonaws.com/ 150 -> 0/150 failed
./corruption-test.py cf 1500 -> 6 events / 140.6 MiB verified
All seven damaged segments across those six transfers were identical: 1178 bytes, entirely zero, contiguous, ending exactly on a segment boundary, 230 bytes of the IP packet kept. One transfer contained two independently damaged segments. Four of the six events landed within six consecutive iterations (1469, 1471, 1473, 1474), which illustrates the clustering described below.
| Suspect | How it was eliminated |
|---|---|
| Client hardware | Same laptop clean on a second Comcast circuit in the same building: 0/400 vs ~10% |
| NIC offloads | TSO and checksum offload disabled, no change |
| MTU / fragmentation | Full 1500-byte MTU confirmed; clamping MSS to 1300 did not help |
| Gateway NAT | IPv6 is not translated and fails at the same rate |
| Gateway state | Power cycle, no change (rate measured before and after) |
| SecurityEdge | Disabled: rate roughly halved but corruption continued, byte-for-byte identical |
| Destination | Uploads to AWS, Google and Facebook clean across 350 MB interleaved with failing Cloudflare uploads |
| Line quality | Downloads clean over 320 MB; the working circuit has more than twice the first-hop jitter |
Failing and working traffic share every Comcast hop up to
be-3211-pe11.910fifteenth.co.ibone.comcast.net, diverging only afterwards.
The March report implicates the equivalent router in Chicago,
be-2211-pe11.350ecermak.il.ibone.comcast.net -- same pe11 role on the same
ibone backbone.
python3 make-payload.py plain 1048576 /tmp/payload1m.bin
# Fails on the affected circuit (expect ~7-12%)
./upload-test.sh https://cloudflare.com/ 150
./upload-test.sh https://cloud.zed.dev/ 150
./upload-test.sh https://discord.com/ 150
# Controls -- expect zero. Run alternately with the above.
./upload-test.sh https://s3.us-west-2.amazonaws.com/ 150
./upload-test.sh https://www.google.com/ 150
# Both address families fail, ruling out NAT
./upload-test.sh https://cloudflare.com/ 100 -4
./upload-test.sh https://cloudflare.com/ 100 -6
# Show the actual corrupted bytes (plain HTTP, echoed back and compared)
./corruption-test.py mss
./corruption-test.py cf 1500 # ~1 event per 50 MB on the affected circuit
./corruption-test.py aws 1500 # control: expect zeroFailures come in bursts. The longest clean run recorded on the faulty circuit was 67 consecutive 1 MiB uploads. A 20-request test passes routinely on a broken line. Run the full 150 and treat a single clean run as inconclusive.
Size matters. Use 1 MiB payloads for pass/fail work. corruption-test.py is
limited to 96 KiB because that is the largest body postman-echo.com accepts,
which is why it needs far more iterations to catch an event.