Skip to content

feat(agent): make the CGNAT range collision visible (metrics + startup detection) - #17

Open
wighawag wants to merge 3 commits into
tunnetio:mainfrom
wighawag:fix/mesh-cidr-collision-diagnostics
Open

feat(agent): make the CGNAT range collision visible (metrics + startup detection)#17
wighawag wants to merge 3 commits into
tunnetio:mainfrom
wighawag:fix/mesh-cidr-collision-diagnostics

Conversation

@wighawag

@wighawag wighawag commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Diagnostics only. No wire format, no protocol change, no behaviour change to the datapath. Splits into four commits that can be dropped independently.

Fixes the reason the 100.64.0.0/10 collision with Tailscale was invisible, and then easy to misdiagnose once noticed.

  1. Every drop reason gets its own counter. Of the 21 reasons the agent can report, only 6 were distinguishable; no_route, ipv6_unsupported, antispoof and 12 others all reported as
    reason="other". A test scans the crate source for dropped_inc("...") call sites and fails if any reason is unregistered, so this cannot silently regress.

  2. Multicast is counted separately from no_route. LAN discovery apps beacon on every interface forever; on one host that was 1.27M no_route drops from ableton-linkd alone, which was read
    as evidence of a routing leak and took a packet capture to disprove.

  3. Startup detection of another interface claiming our mesh CIDR, naming the likely owner and which of our own addresses are consequently unreachable, exported as a gauge.

  4. Design doc recording the investigation, including what the evidence did and did not support.

Related: #15 fixes the MagicDNS symptom, #16 fixes an unrelated multicast leak found while investigating.

Verified with cargo fmt --all -- --check, cargo clippy --workspace --exclude tunnet-desktop --all-targets --all-features -- -D warnings, and cargo test --workspace --exclude tunnet-desktop
--all-features (433 passed, 0 failed). Each commit builds and tests green individually.

@wighawag

wighawag commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Context: this is the diagnostics half of #18. It does not fix the collision, it makes it visible. The metrics gap it closes is what allowed the original report to be misdiagnosed: 21 drop reasons existed but only 6 were distinguishable, and benign multicast was counted as no_route.

Refs #18

Multicast and broadcast are unroutable on the mesh by design, but they were
counted as `no_route`, the same label used when a genuine unicast mesh
destination has no peer.

LAN discovery apps beacon on every interface including ours, forever, at a
steady rate. On one host `ableton-linkd` (224.76.78.75:20808) accounted for
1.27M `no_route` drops over 1d20h at ~8 packets/s, sourced from the mesh
interface's own addresses. A packet capture showed zero unicast mesh
destinations among them. That benign noise was read as evidence of a
routing leak, which cost a full investigation to disprove.

Classify multicast/broadcast as `reason="multicast"` at the drop site so
`no_route` means what a reader assumes it means.
Direct mode puts a connected route for the whole mesh range on the tunnel
interface. Tailscale uses the same 100.64.0.0/10 and installs a
source-based anti-spoof rule:

    -A ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP

Because it matches on SOURCE, it also drops the host's own traffic to our
addresses, including over loopback. Measured on an affected host: 50 of 50
packets to the mesh IP and 50 of 50 to the MagicDNS resolver were dropped,
while a 127.0.0.1 control was untouched. The resolver stays listening and
never receives a query, so MagicDNS fails with no error and no log line.

Neither product reports any of this. The user sees "connected" on both and
traffic that does not work.

Detect at agent start that another interface holds an address overlapping
our mesh CIDR, name the likely owner, and say which of our own addresses
are consequently unreachable. Export it as a gauge so the condition is
visible to monitoring rather than only in a log line.

Detection is a pure function over plain interface data, so it is tested
without a host; the netdev path was verified separately against a machine
running both products. Observation only: nothing here changes routing, and
a failed check never blocks startup.
Captures the reproduction and what the evidence did and did not support,
including that the originally reported no_route figure was disproven (it
was multicast discovery noise) and that the anti-spoof drops were later
confirmed causally with a TCP probe against a live peer.

Notes that tunnetio#24 supersedes the proposal section, and which two defects it
does not address.
@wighawag
wighawag force-pushed the fix/mesh-cidr-collision-diagnostics branch from 80cd2b9 to a776d25 Compare September 6, 2026 15:14
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