Skip to content

fix(proto): canonicalize FourTuple's remote/local_ip at construction (noq#738) - #784

Open
cuzic wants to merge 4 commits into
n0-computer:mainfrom
cuzic:pr/noq-738-canonicalize-at-construction
Open

fix(proto): canonicalize FourTuple's remote/local_ip at construction (noq#738)#784
cuzic wants to merge 4 commits into
n0-computer:mainfrom
cuzic:pr/noq-738-canonicalize-at-construction

Conversation

@cuzic

@cuzic cuzic commented Aug 5, 2026

Copy link
Copy Markdown

Closes #738. Alternative to #783 — see that PR for the minimal one-line fix;
this one takes a broader approach to the same root cause.

Problem

4bae6edd (the hotfix posted in #738) and #783 both patch individual
==/!= comparison sites in Connection::early_discard_packet to
canonicalize IPv4-mapped-IPv6 addresses (::ffff:a.b.c.d) before
comparing, since dual-stack sockets can report the same peer in either
form depending on the code path (observed on Android when a
physical-interface-bound socket is handed to noq via an abstract socket).

Patching comparisons one at a time only closes the specific sites someone
has actually hit a bug in. FourTuple derives PartialEq/Eq/Hash,
and it's used directly (not just via the two early_discard_packet sites)
in:

  • Endpoint's HashMap<FourTuple, ConnectionHandle> connection-routing
    table
  • Connection::is_probably_same_path
  • PATH_CHALLENGE on-path detection
  • OBSERVED_ADDRESS path matching
  • peer migration detection

Every one of these has the same latent bug: two FourTuples that are
"the same address" from the OS's perspective but differ in
mapped-vs-plain representation will compare unequal.

Fix

Canonicalize remote and local_ip once, in FourTuple::new() itself,
so every downstream comparison/hash sees one canonical representation
regardless of which form the OS happened to report a given datagram's
address in. This makes the two early_discard_packet-specific patches
(4bae6edd, #783) unnecessary — they're subsumed by this fix — though
either could still land independently since they're harmless on their
own.

Not claiming this is strictly the better fix — a single well-placed
comparison-time fix (#783) is simpler to reason about and reviews faster.
Filing both so the maintainers can pick.

Testing

Same regression test and full-suite run as #783 (this PR includes the
same test-adaptation commit, since both branches were cut from 4bae6edd
independently):

  • noq-proto/src/tests/multipath.rs::open_path_with_explicit_local_ip
    (from the noq-738 branch) passes; fails on unpatched main.
  • cargo test -p noq-proto: 388 passed, 0 failed.
  • Verified on a real Android device, same scenario as fix(proto): canonicalize remote IP in early_discard_packet's peer check (noq#738) #783Secondary
    established, then PhysicalWifi/PhysicalCellular both validate on
    the first attempt:
    opening path "physical-wifi" -> <redacted>:45823 (local_ip=Some(192.168.10.80), attempt 1/3)
    path "physical-wifi" established: id=PathId(2)
    opening path "physical-cellular" -> <redacted>:45823 (local_ip=Some(10.209.235.90), attempt 1/3)
    path "physical-cellular" established: id=PathId(3)
    

divagant-martian and others added 4 commits July 30, 2026 22:31
…(noq#738)

Alternative to patching each individual == / != comparison site
(early_discard_packet's local_ip check in 4bae6ed, its remote check in
fix/noq-738-remote-canonicalize): canonicalizing once in FourTuple::new()
fixes every downstream comparison for free, including ones that were
still broken after both of the above -- notably Endpoint's
HashMap<FourTuple, ConnectionHandle> connection-routing table, which
hashes/compares FourTuple's derived Hash/Eq directly, plus several other
== / != .remote comparisons in Connection (is_probably_same_path,
PATH_CHALLENGE on-path detection, OBSERVED_ADDRESS path matching, peer
migration detection) that all have the same latent bug.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🚑 Needs Triage

Development

Successfully merging this pull request may close these issues.

open_path() with explicit local_ip: PATH_RESPONSE never reaches on_path_response_received, path stuck ValidationFailed

3 participants