mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-10 00:51:07 +00:00
cmd/tailscale,net/netcheck: add debug feature to force preferred DERP
This provides an interface for a user to force a preferred DERP outcome for all future netchecks that will take precedence unless the forced region is unreachable. The option does not persist and will be lost when the daemon restarts. Updates tailscale/corp#18997 Updates tailscale/corp#24755 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:

committed by
James Tucker

parent
74069774be
commit
7f9ebc0a83
@@ -236,6 +236,10 @@ type Client struct {
|
||||
// If false, the default net.Resolver will be used, with no caching.
|
||||
UseDNSCache bool
|
||||
|
||||
// if non-zero, force this DERP region to be preferred in all reports where
|
||||
// the DERP is found to be reachable.
|
||||
ForcePreferredDERP int
|
||||
|
||||
// For tests
|
||||
testEnoughRegions int
|
||||
testCaptivePortalDelay time.Duration
|
||||
@@ -780,6 +784,12 @@ func (o *GetReportOpts) getLastDERPActivity(region int) time.Time {
|
||||
return o.GetLastDERPActivity(region)
|
||||
}
|
||||
|
||||
func (c *Client) SetForcePreferredDERP(region int) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.ForcePreferredDERP = region
|
||||
}
|
||||
|
||||
// GetReport gets a report. The 'opts' argument is optional and can be nil.
|
||||
// Callers are discouraged from passing a ctx with an arbitrary deadline as this
|
||||
// may cause GetReport to return prematurely before all reporting methods have
|
||||
@@ -1277,6 +1287,9 @@ func (c *Client) logConciseReport(r *Report, dm *tailcfg.DERPMap) {
|
||||
if r.CaptivePortal != "" {
|
||||
fmt.Fprintf(w, " captiveportal=%v", r.CaptivePortal)
|
||||
}
|
||||
if c.ForcePreferredDERP != 0 {
|
||||
fmt.Fprintf(w, " force=%v", c.ForcePreferredDERP)
|
||||
}
|
||||
fmt.Fprintf(w, " derp=%v", r.PreferredDERP)
|
||||
if r.PreferredDERP != 0 {
|
||||
fmt.Fprintf(w, " derpdist=")
|
||||
@@ -1435,6 +1448,21 @@ func (c *Client) addReportHistoryAndSetPreferredDERP(rs *reportState, r *Report,
|
||||
// which undoes any region change we made above.
|
||||
r.PreferredDERP = prevDERP
|
||||
}
|
||||
if c.ForcePreferredDERP != 0 {
|
||||
// If the forced DERP region probed successfully, or has recent traffic,
|
||||
// use it.
|
||||
_, haveLatencySample := r.RegionLatency[c.ForcePreferredDERP]
|
||||
var recentActivity bool
|
||||
if lastHeard := rs.opts.getLastDERPActivity(c.ForcePreferredDERP); !lastHeard.IsZero() {
|
||||
now := c.timeNow()
|
||||
recentActivity = lastHeard.After(rs.start)
|
||||
recentActivity = recentActivity || lastHeard.After(now.Add(-PreferredDERPFrameTime))
|
||||
}
|
||||
|
||||
if haveLatencySample || recentActivity {
|
||||
r.PreferredDERP = c.ForcePreferredDERP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateLatency(m map[int]time.Duration, regionID int, d time.Duration) {
|
||||
|
@@ -201,6 +201,7 @@ func TestAddReportHistoryAndSetPreferredDERP(t *testing.T) {
|
||||
steps []step
|
||||
homeParams *tailcfg.DERPHomeParams
|
||||
opts *GetReportOpts
|
||||
forcedDERP int // if non-zero, force this DERP to be the preferred one
|
||||
wantDERP int // want PreferredDERP on final step
|
||||
wantPrevLen int // wanted len(c.prev)
|
||||
}{
|
||||
@@ -366,12 +367,65 @@ func TestAddReportHistoryAndSetPreferredDERP(t *testing.T) {
|
||||
wantPrevLen: 2,
|
||||
wantDERP: 1, // diff is 11ms, but d2 is greater than 2/3s of d1
|
||||
},
|
||||
{
|
||||
name: "forced_two",
|
||||
steps: []step{
|
||||
{time.Second, report("d1", 2, "d2", 3)},
|
||||
{2 * time.Second, report("d1", 4, "d2", 3)},
|
||||
},
|
||||
forcedDERP: 2,
|
||||
wantPrevLen: 2,
|
||||
wantDERP: 2,
|
||||
},
|
||||
{
|
||||
name: "forced_two_unavailable",
|
||||
steps: []step{
|
||||
{time.Second, report("d1", 2, "d2", 1)},
|
||||
{2 * time.Second, report("d1", 4)},
|
||||
},
|
||||
forcedDERP: 2,
|
||||
wantPrevLen: 2,
|
||||
wantDERP: 1,
|
||||
},
|
||||
{
|
||||
name: "forced_two_no_probe_recent_activity",
|
||||
steps: []step{
|
||||
{time.Second, report("d1", 2)},
|
||||
{2 * time.Second, report("d1", 4)},
|
||||
},
|
||||
opts: &GetReportOpts{
|
||||
GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
|
||||
1: startTime,
|
||||
2: startTime.Add(time.Second),
|
||||
}),
|
||||
},
|
||||
forcedDERP: 2,
|
||||
wantPrevLen: 2,
|
||||
wantDERP: 2,
|
||||
},
|
||||
{
|
||||
name: "forced_two_no_probe_no_recent_activity",
|
||||
steps: []step{
|
||||
{time.Second, report("d1", 2)},
|
||||
{PreferredDERPFrameTime + time.Second, report("d1", 4)},
|
||||
},
|
||||
opts: &GetReportOpts{
|
||||
GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
|
||||
1: startTime,
|
||||
2: startTime,
|
||||
}),
|
||||
},
|
||||
forcedDERP: 2,
|
||||
wantPrevLen: 2,
|
||||
wantDERP: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
fakeTime := startTime
|
||||
c := &Client{
|
||||
TimeNow: func() time.Time { return fakeTime },
|
||||
TimeNow: func() time.Time { return fakeTime },
|
||||
ForcePreferredDERP: tt.forcedDERP,
|
||||
}
|
||||
dm := &tailcfg.DERPMap{HomeParams: tt.homeParams}
|
||||
rs := &reportState{
|
||||
|
Reference in New Issue
Block a user