feat(iroh): reach IPv4 addresses through NAT64 on IPv6-only networks - #4577
Draft
huangyingw wants to merge 1 commit into
Draft
huangyingw wants to merge 1 commit into
huangyingw wants to merge 1 commit into
Conversation
…works On IPv6-only networks with NAT64 (most large mobile carriers, e.g. T-Mobile US), an endpoint has no usable IPv4 socket. When the remote only has IPv4 addresses, the two sides share no address family and the connection stays on the relay forever, even when the remote's port is directly reachable. This translates transparently in the IP transports, like a userspace CLAT (RFC 6877): while a NAT64 prefix is active, datagrams to public IPv4 destinations are sent from the IPv6 socket to the RFC 6052 synthesized address, and datagrams from inside the prefix are reported as coming from the embedded IPv4 address. noq, path management and the remote never see the IPv6 form. Since the relay's QAD probes use the same sockets, IPv4 QAD then succeeds through NAT64 and yields the NAT64 gateway's public address, which is published as a reflexive candidate. This lets the remote hole punch too, so remotes behind an EIM NAT (typical home routers) become reachable as well, not only remotes with a reachable port. net_report decides when to translate: IPv4 QAD failed while the host has IPv6, and either the network's DNS64 reveals the prefix (RFC 7050, ipv4only.arpa), or the host has no IPv4 address besides a CLAT one (192.0.0.0/29), in which case the Well-Known Prefix is assumed. A failed IPv4 QAD on a dual-stack host without DNS64 never diverts native IPv4. Once on, translation stays on until the next major network change, since IPv4 QAD then succeeds through NAT64 and no longer measures native IPv4. The prefix is exposed as `Report::nat64_prefix`. New patchbay tests put an IPv6-only client behind the `IspV6` (NAT64) preset and connect to an IPv4-only server, once with a public address and once behind a Home NAT. Both fail on main (relay only, no direct path within 30s) and pass with this change. Claude-Session: https://claude.ai/code/session_01YRX29ic8ZvvDHLKfF3En1q
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #4576.
IPv6-only endpoints behind NAT64 (most large mobile carriers, e.g. T-Mobile US) can't get a direct path to IPv4-only remotes today: they share no address family, so the connection stays on the relay. This adds NAT64 support so they can.
The translation happens in the IP transports, like a userspace CLAT (RFC 6877):
When to translate is decided in
net_report(Client::update_nat64):ipv4only.arpa, RFC 7050), or192.0.0.0/29), in which case the Well-Known Prefix64:ff9b::/96is assumed. iOS can hold a CLAT address on some interface without offering IPv4 to apps, so plainhave_v4isn't enough here.udp_v6, since a relay may not offer IPv6 QAD even where IPv6 works.udp_v4no longer measures native IPv4.Changes:
net_report/nat64.rs(new): RFC 6052 synthesis/extraction for all six prefix lengths, RFC 7050 prefix discovery,is_translatable, andNat64Stateshared betweennet_reportand the transports (anAtomicBoolfast path, so nothing changes on the hot path when NAT64 is off). Hand-written because therfc6052crate is GPL-3.0.net_report.rs:update_nat64/discover_nat64_prefix; IPv4 QAD also runs while translation is active.net_report/reportgen.rs:IfStateDetails::have_native_v4(IPv4 excluding the CLAT range).socket/transports.rs,socket/transports/ip.rs: translate on send, map back on receive.tests/patchbay/nat64.rs(new): IPv6-only client behindIspV6↔ IPv4-only server, withPublicV4and withHome.API Changes
Report::nat64_prefix: Option<Ipv6Net>(Reportis#[non_exhaustive], so this is additive). It also reflects thatudp_v4/global_v4describe IPv4 through NAT64 while it is set.ipnetnow enables itsserdefeature (for the newReportfield).Notes & open questions
main(no direct path within 30s, relay only) and pass with this change. Full patchbay suite on this branch vsmain, test by test: the only difference is the two new tests (fail onmain, pass here); all other tests have the same result (63 pass, 9 ignored).degrade_client_very_bad_cellularfailed once in the full run on this branch while the machine was under heavy load, then passed 5/5 when rerun on its own (also 5/5 onmain).irohlib tests: 134 passed.cargo fmtandcargo clippy --all-targetsare clean.is_major. If there's a better signal for "the IPv4 situation may have changed", I'd switch to it.Change checklist
proposed change and wrote an as clear and concise description as
they could.
intented effect.