Skip to content

feat(endpoint): add DirectAddrFilter to drop direct address candidates - #4397

Open
ifdario wants to merge 5 commits into
n0-computer:mainfrom
rayfish:direct-addr-filter
Open

feat(endpoint): add DirectAddrFilter to drop direct address candidates#4397
ifdario wants to merge 5 commits into
n0-computer:mainfrom
rayfish:direct-addr-filter

Conversation

@ifdario

@ifdario ifdario commented Jul 7, 2026

Copy link
Copy Markdown

Description

Adds Builder::direct_addr_filter to drop addresses from the endpoint's direct
address candidates. A dropped address is not stored, published, or used for
holepunching / NAT-traversal.

This lets an app exclude a virtual interface's address (e.g. a VPN overlay on a
TUN device) that would otherwise be advertised and dialed by peers, looping the
underlay back into the tunnel. addr_filter only covers published data; this runs
on the candidate set before store_direct_addresses, so it also covers in-band
NAT-traversal.

  • DirectAddrFilter trait (keeps(&self, ip) -> bool), modelled on PathSelector.
  • Builder::direct_addr_filter, stored on Options/Socket, applied in
    update_direct_addresses.
  • Tests for a reject-all and a reject-none filter.

Breaking Changes

None. Optional, defaults to off.

Notes & open questions

Applied at the final gate so it covers all sources (local, QAD, portmap, config).
Could be scoped to local-interface addresses instead if preferred.

Change checklist

  • Self-review.
  • Documentation updates following the style guide, if relevant.
  • Tests if relevant.
  • All breaking changes documented.
  • This PR was created by a human that thought critically about the
    proposed change and wrote an as clear and concise description as
    they could.
  • This PR isn't slop, and is carefully crafted to do have the
    intented effect.

Add `Builder::direct_addr_filter`, taking a `DirectAddrFilter` trait object
(`Arc<dyn>`, mirroring `PathSelector`), consulted in the magicsock's
`update_direct_addresses` before the direct addresses are stored. A rejected
address is never stored, published via Address Lookup, or offered as a
holepunch / NAT-traversal candidate.

This lets an application exclude the address of a virtual interface (e.g. a
VPN overlay bound on a TUN device) from the candidate set while still binding
to the unspecified address for normal multi-homing. `addr_filter` only filters
published data; this filters the underlying candidate set, so it also covers
in-band NAT-traversal.
Add a blanket `impl DirectAddrFilter for Vec<IpNet>` so whole CIDR ranges can
be excluded without implementing the trait, plus `Builder::exclude_direct_addrs`
as a convenience wrapper over it for the common case (e.g. dropping a VPN
overlay bound on a TUN device).
@flub

flub commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

For the random observer: this is linked to #4399 and we need to decide which approach is best.

@flub flub added NAT Traversal c-iroh Functionality of the core iroh crate. labels Jul 8, 2026
@ifdario

ifdario commented Jul 8, 2026

Copy link
Copy Markdown
Author

I think they fill different needs

`exclude_direct_addrs` now wraps a private `ExcludeNets` newtype instead of
a public `impl DirectAddrFilter for Vec<IpNet>`, which kept a trait impl on
a foreign type out of the public API. `IpNet` is re-exported from
`iroh::endpoint` so callers do not need a matching `ipnet` of their own, and
`ipnet::*` joins the accepted non-1.0 crates in `allowed_external_types`
(the check fails on the `exclude_direct_addrs` signature otherwise).

Also documents the filter as covering the whole candidate set rather than
just local interface addresses, borrows it instead of cloning the `Arc` on
every update, and imports `DirectAddrFilter` instead of naming it inline.
GrizzlT pushed a commit to GrizzlT/rayfish that referenced this pull request Aug 31, 2026
Use iroh's new `Builder::direct_addr_filter` to keep rayfish overlay addresses
(100.64.0.0/10, 200::/7) out of the gathered direct-address set, so a mesh IP
bound on the TUN is never advertised, published, or offered as a holepunch /
NAT-traversal candidate. Previously the overlay IP leaked into the candidate set
and peers dialed it, looping the underlay back through the tunnel (a flapping
path that fell back to relay latency).

Pins iroh (and iroh-base/-dns/-relay) to the fork branch carrying the API,
upstream PR n0-computer/iroh#4397.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c-iroh Functionality of the core iroh crate. NAT Traversal

Projects

Status: 🚑 Needs Triage

Development

Successfully merging this pull request may close these issues.

2 participants