From: anonreporting <anonreporting@disroot.org>
To: linux-usb@vger.kernel.org
Subject: cdc_ncm: short frames not padded to ETH_ZLEN, ARP broken with strict routers
Date: Sat, 30 May 2026 18:35:33 +0200 [thread overview]
Message-ID: <268e09ae8e9d02bf976f8cf0793fd0d8@disroot.org> (raw)
Hi,
I've been chasing a connectivity issue with a USB-C dock (DisplayLink,
VID 17e9:6000,
driver cdc_ncm) and eventually tracked it down to the driver
transmitting Ethernet frames
shorter than ETH_ZLEN without zero-padding them.
== Symptom ==
The interface gets a DHCP lease fine (BOOTP frames are large enough),
but every attempt
to reach the default gateway fails with 100% packet loss. A Fritz!Box
router is involved
on the other end.
tcpdump on the cdc_ncm interface shows outgoing ARP requests at 42
bytes:
14:03:07.112233 00:xx:xx:xx:xx:xx > ff:ff:ff:ff:ff:ff, ethertype ARP
(0x0806), \
length 42: Request who-has 192.168.1.1 tell 192.168.1.x, length 28
Same ARP from an r8152 interface on the same machine (same cable, same
switch port):
14:03:07.112400 00:yy:yy:yy:yy:yy > ff:ff:ff:ff:ff:ff, ethertype ARP
(0x0806), \
length 58: Request who-has 192.168.1.1 tell 192.168.1.x, length 44
So r8152 pads the 28-byte ARP payload to 44 bytes (16 bytes of zeros),
giving a 58-byte
frame. cdc_ncm sends it raw at 42 bytes. IEEE 802.3 minimum is 60 bytes
without FCS.
The Fritz!Box drops anything below ~50 bytes; 58-byte frames are
accepted, 42-byte ones
are not.
== Root cause ==
The cdc_ncm TX path in cdc_ncm_fill_tx_frame() packs SKBs into NCM
Transfer Blocks
(NTBs) without ensuring each frame meets ETH_ZLEN. An ARP request
arrives as a
42-byte SKB and is placed into the NTB verbatim:
skb_put_data(skb_out, skb->data, skb->len);
ctx->tx_curr_frame_payload += skb->len;
dev_kfree_skb_any(skb);
Neither eth_skb_pad() nor ETH_ZLEN appears anywhere in the cdc_ncm TX
path
(verified against current tree). Other USB Ethernet drivers — r8152
being the
working reference here — call eth_skb_pad() before the frame reaches
hardware.
== Suggested fix (untested, no patch built) ==
The padding needs to happen before skb->len is first used for NDP space
reservation
(the cdc_ncm_ndp16/ndp32 call), not just before skb_put_data(). The
following is an
untested sketch:
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ cdc_ncm_fill_tx_frame(), before the NDP allocation block:
+ /* ensure frame meets IEEE 802.3 minimum; eth_skb_pad() frees skb on
failure */
+ if (skb->len < ETH_ZLEN && eth_skb_pad(skb)) {
+ skb = NULL;
+ dev->net->stats.tx_dropped++;
+ break;
+ }
+
/* get the appropriate NDP for this skb */
if (ctx->is_ndp16)
ndp.ndp16 = cdc_ncm_ndp16(ctx, skb_out, sign,
skb->len + ctx->tx_modulus + ctx->tx_remainder);
else
ndp.ndp32 = cdc_ncm_ndp32(ctx, skb_out, sign,
skb->len + ctx->tx_modulus + ctx->tx_remainder);
This placement ensures skb->len already reflects the padded length when
NDP space
is reserved, when the fit check runs, and when wDatagramLength is
written into the
NDP entry. The break is conservative on alloc failure — it sends the
current NTB
without the dropped frame rather than risking use-after-free.
I have not compiled or tested this — leaving it to someone who knows the
error
paths in cdc_ncm_fill_tx_frame() better than I do.
== Notes ==
- DHCP frames are unaffected (large enough), so the issue only shows up
after the
interface is up and the kernel starts sending ARP — which can make it
look like a
routing or ARP cache problem rather than a driver issue.
- The exact same hardware works correctly when the Fritz!Box is replaced
with a switch
that is more lenient about frame size, which was another red herring
during diagnosis.
- Kernel: 6.12.74+deb13+1-amd64 (Debian 13)
- Affected: DisplayLink USB-C Triple-4K Dock, 17e9:6000, cdc_ncm
- Working reference: Realtek RTL8153, 0bda:8153, r8152
I'm happy to test patches.
Regards,
anonreporting <anonreporting@disroot.org>
reply other threads:[~2026-05-30 16:35 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=268e09ae8e9d02bf976f8cf0793fd0d8@disroot.org \
--to=anonreporting@disroot.org \
--cc=linux-usb@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox