Skip to content

Unvalidated advertised endpoints installed at peer-add — one-way tunnel with CGNAT peers #18

Description

@snowpaper

Summary

ml_wg_mgr.c installs a peer's advertised endpoint (endpoints[0] from the MapResponse) as the WireGuard endpoint at peer-add time, before any path validation. wireguardif's output path treats any non-zero endpoint as a working direct path. For a peer behind carrier-grade NAT (e.g. a laptop on a phone hotspot) the advertised endpoint is unreachable — no hole was ever punched — so the tunnel goes one-way: inbound arrives via DERP, but every outbound data packet is sent direct-UDP into a black hole.

Observed symptom: tailscale status shows the node active, the peer's pings/TCP SYNs reach the node (via DERP), but all replies are lost. Ping times out, TCP to the node's service never connects.

Environment

  • ESP32-S3, ESP-IDF v5.3, microlink @ 216da33 (code unchanged on current main)
  • Peer: Windows PC with official Tailscale client on a cellular hotspot (symmetric CGNAT)

Root cause

components/microlink/src/ml_wg_mgr.c ~line 500: at peer-add, if endpoint_count > 0 the advertised endpoints[0] is copied into wg_peer.endpoint_ip/endport_port. has_direct_path is still false — no direct DISCO pong ever arrived — yet data TX uses the endpoint. Packet trace from the device: inbound data frames arrive via_derp=1, outbound data goes direct-UDP to the advertised CGNAT address, DERP TX carries zero data (hdr=04) frames.

Fix

Tailscale's own semantics: data flows via DERP until a direct path is validated by a DISCO pong. Fix is to leave the WG endpoint blank at peer-add; the only endpoint installs are the already-correctly-gated ones (process_disco_pong on a direct pong, and the handshake path gated on best_ip). Same-LAN peers still upgrade to direct within seconds because the direct pong validates immediately. ~14-line change; PR follows.

Verification

On hardware: before the fix, a CGNAT peer could never reach a TCP service on the node (ping timeout, connect fail) despite the node showing as online. After the fix: ping replies over DERP, TCP connects, a full print job relayed end-to-end over the DERP path. Same-LAN direct path unaffected (validated same day).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions