Skip to content

fix(rndis): canonical packet framing and filter re-apply for vivo/MediaTek - #10

Open
prodzaha wants to merge 1 commit into
HelloPrincePal:mainfrom
prodzaha:fix/vivo-rndis-compat
Open

prodzaha wants to merge 1 commit into
HelloPrincePal:mainfrom
prodzaha:fix/vivo-rndis-compat

Conversation

@prodzaha

Copy link
Copy Markdown

Problem

With a vivo X200 Pro mini (MediaTek) on macOS 26 the tunnel never comes up: the phone keeps rx_packets=0 while rx_errs grows, and both the DHCP Discover and the keepalive frames are dropped. Every RNDIS data frame is rejected by the phone.

Root causes found on the wire:

  1. Non-canonical framing. Frames were sent as REMOTE_NDIS_PACKET_MSG with a 48-byte header and DataOffset = 40. The vivo gadget rejects those as receive errors. The in-tree Linux host driver (rndis_host.c) uses a 44-byte header with DataOffset = 36, and the same device accepts it immediately (verified by A/B testing both layouts byte-for-byte, same session, repeated).
  2. Packet filter reset. Issuing SET_INTERFACE on the data interface resets OID_GEN_CURRENT_PACKET_FILTER to 0 on this device, so the phone silently stops sending broadcast traffic — the DHCP offer never leaves it (tx_packets=0). Re-applying the filter after the interface is open fixes it: before, zero replies; after, the DHCP OFFER arrives.
  3. Stale control responses. A previous host session can leave completions queued on the control pipe; the next handshake then reads them out of order and desyncs INIT/QUERY/SET.

Changes

  • EncapsulatePacket emits the canonical 44-byte header (DataOffset 36) and ships with a unit test pinning the layout and the decode round-trip.
  • Drain stale encapsulated control responses before the handshake.
  • SET_INTERFACE(alt 0) plus CLEAR_FEATURE(HALT) on the data endpoints before first use (some gadgets keep endpoints disabled until SET_INTERFACE; a halted pipe NAKs until it times out).
  • Re-apply the RNDIS packet filter once the data interface has been opened.

Testing

  • go test ./... — new internal/rndis tests pass.
  • Runtime (macOS 26.6, Apple Silicon, vivo X200 Pro mini): DHCP OFFER/ACK complete, the utun interface is configured, ping/curl over the USB link work.
  • Compatibility note: 44/36 is what the Linux and Windows host stacks send, so this is expected to be at least as compatible as the previous padding. The 48/40 layout is reported to work on some Xiaomi/Samsung devices; that path is unchanged in every other respect.

The crash-on-disconnect fix is in the companion PR.

…iaTek

Frames were sent as REMOTE_NDIS_PACKET_MSG with a 48-byte header and
DataOffset 40. The vivo X200 Pro mini (MediaTek) rejects those frames:
they are counted as receive errors and dropped, so the relay never sees
a DHCP offer and the tunnel never comes up. The in-tree Linux host
driver (rndis_host.c) uses a 44-byte header with DataOffset 36, and the
same device accepts it immediately. Switch to the canonical layout and
add a unit test that pins it.

Additionally:
- drain stale encapsulated control responses before the handshake; the
  device can otherwise hand back completions from a previous session and
  desync INIT/QUERY/SET;
- select the data interface explicitly (SET_INTERFACE alt 0) and clear
  any stale endpoint halt before first use: some gadgets keep their bulk
  endpoints disabled until SET_INTERFACE, and transfers to a halted pipe
  NAK until they time out;
- re-apply OID_GEN_CURRENT_PACKET_FILTER after the data interface has
  been opened. SET_INTERFACE resets the filter to zero on vivo, which
  makes the phone drop its own broadcast traffic (DHCP offers) silently.

Tested against vivo X200 Pro mini on macOS 26 (Apple Silicon): DHCP
OFFER/ACK complete and traffic flows over the USB link.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant