added: self-reported LAN endpoints for peers behind the same NAT #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "lan-endpoints"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Two peers behind the same router never learned an address that works between them. Every address they heard about came from someone outside their LAN, and outsiders only ever see the router's address, never a peer's LAN address. In the original scenario:
Neither works if the router doesn't loop traffic between two of its own LAN devices back through itself, which many routers don't.
How it works
1. Each client reports its own LAN address
Every time a client reports the addresses it has seen other peers at, it also reports itself.
Device.GetLocalEndpointprovides the address: it asks the OS which local address it would use to reach the server, then pairs that with the port WireGuard is actually listening on, including a port the OS picked.Finding the address sends no packets and does no DNS lookup, because the server's address is already resolved.
netaddr.ReachableFromLANthrows away addresses no LAN neighbor could use: loopback, link-local, multicast, and addresses inside the cord network itself.2. The server stores it like any other sighting
A self-report is a normal row in the server's
endpointtable. Peer B is listed as the witness of its own address. The table keeps one row per witness and peer, so each new self-report replaces the last one. The server's code for receiving reports didn't need to change.3. The server shares it only with peers on the same network
When peer A asks the server for its peer list, the server checks whether A and B seem to be behind the same router. That's true when some third peer has seen both A and B coming from the same IP address in the last 15 minutes.
A few details:
self_reportedflag. Older clients ignore it.How the check behaves in practice:
main4. The client tries the most trustworthy addresses first
For each address it knows, the client stores how confident it is that the address works, and when it last had that evidence. There are three levels:
When new information about a known address arrives:
The client tries addresses highest level first, newest first within a level. So a LAN address from the same network is tried right after addresses that have already worked.
In the code,
PeerEndpointis split in two:EndpointObservationis what gets saved, andEndpointCandidateis what the client reads back when choosing an address to try. The store methods are renamed to match.Commits
added: self-reported LAN endpoints for peers behind the same NAT: the feature as first drafted.refactor: folded the LAN endpoint lookup into the network report: removeslan.goand addsnetaddr.ReachableFromLAN.refactor: asked the WireGuard device for its LAN endpoint: replaces theruntime.Options.LocalAddrfunction with a device method, namedGetLocalEndpointas of commit 6.runtime.goand the test setup files are back to matchingmain.changed: ranked client endpoints by confidence: replaces two timestamps and a flag with the confidence levels.refactor: grouped the same-site helpers after the snapshot code: moves code withintopology.gowithout changing it.refactor: tidied the LAN endpoint change after review: moves the last inline SQL out of a transaction, gives functions verb names, simplifies the client migration, and drops two internal tests.Commit 3 removes code that commit 1 added. Reviewing them together, or reviewing the whole diff, saves reading code that no longer exists.
Worth a look in review
mainalready does this, but this PR makes it happen more often. The obvious fix breaks setups where the server itself is on the LAN, so I've left it for a follow-up.macos-support. That branch also editsinternal/client/runtime/network.go,runtime.goandinternal/wireguard/backend_userspace.go.Testing
make lintandmake test-fullpass.self_reportedflag in the peer list, and storing a peer's report of itself with the peer as witness. Turning the check off makes all five "No" cases fail.Device.GetLocalEndpointwith the mock backend, and parsing the listen port from the userspace backend's status.main: one race-detector run failed inTestConfirmInstall_ClosesTemporaryTunnelBeforePublishingNetwork, where the test's cleanup changes the mock while the client is still closing it. It fails the same way on unmodifiedmain, once in 20 runs. The rerun passed.GetLocalEndpointon the real kernel and userspace backends hasn't been run, since both need root. The OS address lookup inside it was checked by hand on a machine with several network interfaces, where it picked the Ethernet address.🤖 Generated with Claude Code