|
|
@@ -23,6 +23,7 @@ import (
|
|
|
"syscall"
|
|
|
"time"
|
|
|
|
|
|
+ "tailscale.com/derp"
|
|
|
"tailscale.com/derp/derphttp"
|
|
|
"tailscale.com/envknob"
|
|
|
"tailscale.com/hostinfo"
|
|
|
@@ -449,7 +450,7 @@ func makeProbePlan(dm *tailcfg.DERPMap, ifState *netmon.State, last *Report, pre
|
|
|
// restoration back to the home DERP on the next full netcheck ~5 minutes later
|
|
|
// - which is highly disruptive when it causes shifts in geo routed subnet
|
|
|
// routers. By always including the home DERP in the incremental netcheck, we
|
|
|
- // ensure that the home DERP is always probed, even if it observed a recenet
|
|
|
+ // ensure that the home DERP is always probed, even if it observed a recent
|
|
|
// poor latency sample. This inclusion enables the latency history checks in
|
|
|
// home DERP selection to still take effect.
|
|
|
// planContainsHome indicates whether the home DERP has been added to the probePlan,
|
|
|
@@ -989,7 +990,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap, opts *GetRe
|
|
|
var wg sync.WaitGroup
|
|
|
var need []*tailcfg.DERPRegion
|
|
|
for rid, reg := range dm.Regions {
|
|
|
- if !rs.haveRegionLatency(rid) && regionHasDERPNode(reg) {
|
|
|
+ if !rs.haveRegionLatency(rid) && regionHasDERPNode(reg) && !reg.Avoid && !reg.NoMeasureNoHome {
|
|
|
need = append(need, reg)
|
|
|
}
|
|
|
}
|
|
|
@@ -1371,6 +1372,15 @@ const (
|
|
|
// even without receiving a STUN response.
|
|
|
// Note: must remain higher than the derp package frameReceiveRecordRate
|
|
|
PreferredDERPFrameTime = 8 * time.Second
|
|
|
+ // PreferredDERPKeepAliveTimeout is 2x the DERP Keep Alive timeout. If there
|
|
|
+ // is no latency data to make judgements from, but we have heard from our
|
|
|
+ // current DERP region inside of 2x the KeepAlive window, don't switch DERP
|
|
|
+ // regions yet, keep the current region. This prevents region flapping /
|
|
|
+ // home DERP removal during short periods of packet loss where the DERP TCP
|
|
|
+ // connection may itself naturally recover.
|
|
|
+ // TODO(raggi): expose shared time bounds from the DERP package rather than
|
|
|
+ // duplicating them here.
|
|
|
+ PreferredDERPKeepAliveTimeout = 2 * derp.KeepAlive
|
|
|
)
|
|
|
|
|
|
// addReportHistoryAndSetPreferredDERP adds r to the set of recent Reports
|
|
|
@@ -1455,13 +1465,10 @@ func (c *Client) addReportHistoryAndSetPreferredDERP(rs *reportState, r *Report,
|
|
|
// the STUN probe) since we started the netcheck, or in the past 2s, as
|
|
|
// another signal for "this region is still working".
|
|
|
heardFromOldRegionRecently := false
|
|
|
+ prevRegionLastHeard := rs.opts.getLastDERPActivity(prevDERP)
|
|
|
if changingPreferred {
|
|
|
- if lastHeard := rs.opts.getLastDERPActivity(prevDERP); !lastHeard.IsZero() {
|
|
|
- now := c.timeNow()
|
|
|
-
|
|
|
- heardFromOldRegionRecently = lastHeard.After(rs.start)
|
|
|
- heardFromOldRegionRecently = heardFromOldRegionRecently || lastHeard.After(now.Add(-PreferredDERPFrameTime))
|
|
|
- }
|
|
|
+ heardFromOldRegionRecently = prevRegionLastHeard.After(rs.start)
|
|
|
+ heardFromOldRegionRecently = heardFromOldRegionRecently || prevRegionLastHeard.After(now.Add(-PreferredDERPFrameTime))
|
|
|
}
|
|
|
|
|
|
// The old region is accessible if we've heard from it via a non-STUN
|
|
|
@@ -1488,17 +1495,20 @@ func (c *Client) addReportHistoryAndSetPreferredDERP(rs *reportState, r *Report,
|
|
|
// 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))
|
|
|
- }
|
|
|
+ lastHeard := rs.opts.getLastDERPActivity(c.ForcePreferredDERP)
|
|
|
+ recentActivity := lastHeard.After(rs.start)
|
|
|
+ recentActivity = recentActivity || lastHeard.After(now.Add(-PreferredDERPFrameTime))
|
|
|
|
|
|
if haveLatencySample || recentActivity {
|
|
|
r.PreferredDERP = c.ForcePreferredDERP
|
|
|
}
|
|
|
}
|
|
|
+ // If there was no latency data to make judgements on, but there is an
|
|
|
+ // active DERP connection that has at least been doing KeepAlive recently,
|
|
|
+ // keep it, rather than dropping it.
|
|
|
+ if r.PreferredDERP == 0 && prevRegionLastHeard.After(now.Add(-PreferredDERPKeepAliveTimeout)) {
|
|
|
+ r.PreferredDERP = prevDERP
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func updateLatency(m map[int]time.Duration, regionID int, d time.Duration) {
|