ipn/ipnlocal: consider all DERP regions for exit node recommendations
When recommending an exit node, suggestExitNodeLocked ranks candidates by the latency to their home DERP region, taken from the most recent netcheck report. But netcheck alternates between full reports, which probe every region, and incremental reports, which only re-probe the home region and a handful of the fastest regions. When the most recent report is incremental, the suggestion fell back to a random for exit nodes that are far away. Now we rank candidates against the best recent latency, tracked by the `netcheck.Client` - the same data that is used to pick the preferred DERP. It uses a history of measurements which includes a full netcheck report, so should cover all DERP regions. Updates tailscale/corp#17516 Signed-off-by: Anton Tolchanov <anton@tailscale.com>
This commit is contained in:
committed by
Anton Tolchanov
parent
6a275c01db
commit
f442cda999
@@ -4345,10 +4345,35 @@ func (c *Conn) GetLastNetcheckReport(ctx context.Context) *netcheck.Report {
|
||||
return c.lastNetCheckReport.Load()
|
||||
}
|
||||
|
||||
// SetLastNetcheckReportForTest sets the magicsock conn's last netcheck report.
|
||||
// Used for testing purposes.
|
||||
func (c *Conn) SetLastNetcheckReportForTest(ctx context.Context, report *netcheck.Report) {
|
||||
c.lastNetCheckReport.Store(report)
|
||||
// AddNetcheckReportForTest records report in the conn's netcheck client's
|
||||
// recent-report history as if it had been produced at time now, seeding the
|
||||
// netcheck client's per-region latency history. If report is newer than the
|
||||
// currently stored last netcheck report, it also becomes the last netcheck
|
||||
// report.
|
||||
func (c *Conn) AddNetcheckReportForTest(dm *tailcfg.DERPMap, report *netcheck.Report, now time.Time) {
|
||||
testenv.AssertInTest()
|
||||
rep := report.Clone() // netchecker mutates the report, so create a copy
|
||||
c.netChecker.AddReportHistoryForTest(dm, rep, now)
|
||||
for {
|
||||
if cur := c.lastNetCheckReport.Load(); cur == nil || rep.Now.After(cur.Now) {
|
||||
if c.lastNetCheckReport.CompareAndSwap(cur, rep) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetDERPRegionLatency returns the lowest latency seen per DERP region over
|
||||
// netcheck's recent history, keyed by region ID. Unlike the most recent report
|
||||
// from GetLastNetcheckReport (which for an incremental netcheck covers only a
|
||||
// few regions), netcheck's history retains every region measured by the most
|
||||
// recent full netcheck, so this can rank regions the latest report did not
|
||||
// re-probe. It returns nil if the netcheck client is not yet initialized.
|
||||
func (c *Conn) GetDERPRegionLatency() map[int]time.Duration {
|
||||
if c.netChecker == nil {
|
||||
return nil
|
||||
}
|
||||
return c.netChecker.RecentRegionLatency()
|
||||
}
|
||||
|
||||
// lazyEndpoint is a wireguard [conn.Endpoint] for when magicsock received a
|
||||
|
||||
Reference in New Issue
Block a user