Skip to content

Commit 0047fcf

Browse files
committed
Merge tag 'v1.76.6' into sunos-1.76
2 parents 627c808 + 1edcf9d commit 0047fcf

File tree

6 files changed

+114
-23
lines changed

6 files changed

+114
-23
lines changed

VERSION.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.76.3
1+
1.76.6

health/health.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1051,11 +1051,15 @@ func (t *Tracker) updateBuiltinWarnablesLocked() {
10511051
ArgDuration: d.Round(time.Second).String(),
10521052
})
10531053
}
1054-
} else {
1054+
} else if homeDERP != 0 {
10551055
t.setUnhealthyLocked(noDERPConnectionWarnable, Args{
10561056
ArgDERPRegionID: fmt.Sprint(homeDERP),
10571057
ArgDERPRegionName: t.derpRegionNameLocked(homeDERP),
10581058
})
1059+
} else {
1060+
// No DERP home yet determined yet. There's probably some
1061+
// other problem or things are just starting up.
1062+
t.setHealthyLocked(noDERPConnectionWarnable)
10591063
}
10601064

10611065
if !t.ipnWantRunning {

net/netcheck/netcheck.go

+53-15
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,11 @@ type probePlan map[string][]probe
391391
// sortRegions returns the regions of dm first sorted
392392
// from fastest to slowest (based on the 'last' report),
393393
// end in regions that have no data.
394-
func sortRegions(dm *tailcfg.DERPMap, last *Report) (prev []*tailcfg.DERPRegion) {
394+
func sortRegions(dm *tailcfg.DERPMap, last *Report, preferredDERP int) (prev []*tailcfg.DERPRegion) {
395395
prev = make([]*tailcfg.DERPRegion, 0, len(dm.Regions))
396396
for _, reg := range dm.Regions {
397-
if reg.Avoid {
397+
// include an otherwise avoid region if it is the current preferred region
398+
if reg.Avoid && reg.RegionID != preferredDERP {
398399
continue
399400
}
400401
prev = append(prev, reg)
@@ -419,9 +420,19 @@ func sortRegions(dm *tailcfg.DERPMap, last *Report) (prev []*tailcfg.DERPRegion)
419420
// a full report, all regions are scanned.)
420421
const numIncrementalRegions = 3
421422

422-
// makeProbePlan generates the probe plan for a DERPMap, given the most
423-
// recent report and whether IPv6 is configured on an interface.
424-
func makeProbePlan(dm *tailcfg.DERPMap, ifState *netmon.State, last *Report) (plan probePlan) {
423+
// makeProbePlan generates the probe plan for a DERPMap, given the most recent
424+
// report and the current home DERP. preferredDERP is passed independently of
425+
// last (report) because last is currently nil'd to indicate a desire for a full
426+
// netcheck.
427+
//
428+
// TODO(raggi,jwhited): refactor the callers and this function to be more clear
429+
// about full vs. incremental netchecks, and remove the need for the history
430+
// hiding. This was avoided in an incremental change due to exactly this kind of
431+
// distant coupling.
432+
// TODO(raggi): change from "preferred DERP" from a historical report to "home
433+
// DERP" as in what DERP is the current home connection, this would further
434+
// reduce flap events.
435+
func makeProbePlan(dm *tailcfg.DERPMap, ifState *netmon.State, last *Report, preferredDERP int) (plan probePlan) {
425436
if last == nil || len(last.RegionLatency) == 0 {
426437
return makeProbePlanInitial(dm, ifState)
427438
}
@@ -432,9 +443,34 @@ func makeProbePlan(dm *tailcfg.DERPMap, ifState *netmon.State, last *Report) (pl
432443
had4 := len(last.RegionV4Latency) > 0
433444
had6 := len(last.RegionV6Latency) > 0
434445
hadBoth := have6if && had4 && had6
435-
for ri, reg := range sortRegions(dm, last) {
436-
if ri == numIncrementalRegions {
437-
break
446+
// #13969 ensure that the home region is always probed.
447+
// If a netcheck has unstable latency, such as a user with large amounts of
448+
// bufferbloat or a highly congested connection, there are cases where a full
449+
// netcheck may observe a one-off high latency to the current home DERP. Prior
450+
// to the forced inclusion of the home DERP, this would result in an
451+
// incremental netcheck following such an event to cause a home DERP move, with
452+
// restoration back to the home DERP on the next full netcheck ~5 minutes later
453+
// - which is highly disruptive when it causes shifts in geo routed subnet
454+
// routers. By always including the home DERP in the incremental netcheck, we
455+
// ensure that the home DERP is always probed, even if it observed a recenet
456+
// poor latency sample. This inclusion enables the latency history checks in
457+
// home DERP selection to still take effect.
458+
// planContainsHome indicates whether the home DERP has been added to the probePlan,
459+
// if there is no prior home, then there's no home to additionally include.
460+
planContainsHome := preferredDERP == 0
461+
for ri, reg := range sortRegions(dm, last, preferredDERP) {
462+
regIsHome := reg.RegionID == preferredDERP
463+
if ri >= numIncrementalRegions {
464+
// planned at least numIncrementalRegions regions and that includes the
465+
// last home region (or there was none), plan complete.
466+
if planContainsHome {
467+
break
468+
}
469+
// planned at least numIncrementalRegions regions, but not the home region,
470+
// check if this is the home region, if not, skip it.
471+
if !regIsHome {
472+
continue
473+
}
438474
}
439475
var p4, p6 []probe
440476
do4 := have4if
@@ -445,7 +481,7 @@ func makeProbePlan(dm *tailcfg.DERPMap, ifState *netmon.State, last *Report) (pl
445481
tries := 1
446482
isFastestTwo := ri < 2
447483

448-
if isFastestTwo {
484+
if isFastestTwo || regIsHome {
449485
tries = 2
450486
} else if hadBoth {
451487
// For dual stack machines, make the 3rd & slower nodes alternate
@@ -456,14 +492,15 @@ func makeProbePlan(dm *tailcfg.DERPMap, ifState *netmon.State, last *Report) (pl
456492
do4, do6 = false, true
457493
}
458494
}
459-
if !isFastestTwo && !had6 {
495+
if !regIsHome && !isFastestTwo && !had6 {
460496
do6 = false
461497
}
462498

463-
if reg.RegionID == last.PreferredDERP {
499+
if regIsHome {
464500
// But if we already had a DERP home, try extra hard to
465501
// make sure it's there so we don't flip flop around.
466502
tries = 4
503+
planContainsHome = true
467504
}
468505

469506
for try := 0; try < tries; try++ {
@@ -788,9 +825,10 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap, opts *GetRe
788825
c.curState = rs
789826
last := c.last
790827

791-
// Even if we're doing a non-incremental update, we may want to try our
792-
// preferred DERP region for captive portal detection. Save that, if we
793-
// have it.
828+
// Extract preferredDERP from the last report, if available. This will be used
829+
// in captive portal detection and DERP flapping suppression. Ideally this would
830+
// be the current active home DERP rather than the last report preferred DERP,
831+
// but only the latter is presently available.
794832
var preferredDERP int
795833
if last != nil {
796834
preferredDERP = last.PreferredDERP
@@ -847,7 +885,7 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap, opts *GetRe
847885

848886
var plan probePlan
849887
if opts == nil || !opts.OnlyTCP443 {
850-
plan = makeProbePlan(dm, ifState, last)
888+
plan = makeProbePlan(dm, ifState, last, preferredDERP)
851889
}
852890

853891
// If we're doing a full probe, also check for a captive portal. We

net/netcheck/netcheck_test.go

+40-2
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,52 @@ func TestMakeProbePlan(t *testing.T) {
576576
"region-3-v4": []probe{p("3a", 4)},
577577
},
578578
},
579+
{
580+
// #13969: ensure that the prior/current home region is always included in
581+
// probe plans, so that we don't flap between regions due to a single major
582+
// netcheck having excluded the home region due to a spuriously high sample.
583+
name: "ensure_home_region_inclusion",
584+
dm: basicMap,
585+
have6if: true,
586+
last: &Report{
587+
RegionLatency: map[int]time.Duration{
588+
1: 50 * time.Millisecond,
589+
2: 20 * time.Millisecond,
590+
3: 30 * time.Millisecond,
591+
4: 40 * time.Millisecond,
592+
},
593+
RegionV4Latency: map[int]time.Duration{
594+
1: 50 * time.Millisecond,
595+
2: 20 * time.Millisecond,
596+
},
597+
RegionV6Latency: map[int]time.Duration{
598+
3: 30 * time.Millisecond,
599+
4: 40 * time.Millisecond,
600+
},
601+
PreferredDERP: 1,
602+
},
603+
want: probePlan{
604+
"region-1-v4": []probe{p("1a", 4), p("1a", 4, 60*ms), p("1a", 4, 220*ms), p("1a", 4, 330*ms)},
605+
"region-1-v6": []probe{p("1a", 6), p("1a", 6, 60*ms), p("1a", 6, 220*ms), p("1a", 6, 330*ms)},
606+
"region-2-v4": []probe{p("2a", 4), p("2b", 4, 24*ms)},
607+
"region-2-v6": []probe{p("2a", 6), p("2b", 6, 24*ms)},
608+
"region-3-v4": []probe{p("3a", 4), p("3b", 4, 36*ms)},
609+
"region-3-v6": []probe{p("3a", 6), p("3b", 6, 36*ms)},
610+
"region-4-v4": []probe{p("4a", 4)},
611+
},
612+
},
579613
}
580614
for _, tt := range tests {
581615
t.Run(tt.name, func(t *testing.T) {
582616
ifState := &netmon.State{
583617
HaveV6: tt.have6if,
584618
HaveV4: !tt.no4,
585619
}
586-
got := makeProbePlan(tt.dm, ifState, tt.last)
620+
preferredDERP := 0
621+
if tt.last != nil {
622+
preferredDERP = tt.last.PreferredDERP
623+
}
624+
got := makeProbePlan(tt.dm, ifState, tt.last, preferredDERP)
587625
if !reflect.DeepEqual(got, tt.want) {
588626
t.Errorf("unexpected plan; got:\n%v\nwant:\n%v\n", got, tt.want)
589627
}
@@ -756,7 +794,7 @@ func TestSortRegions(t *testing.T) {
756794
report.RegionLatency[3] = time.Second * time.Duration(6)
757795
report.RegionLatency[4] = time.Second * time.Duration(0)
758796
report.RegionLatency[5] = time.Second * time.Duration(2)
759-
sortedMap := sortRegions(unsortedMap, report)
797+
sortedMap := sortRegions(unsortedMap, report, 0)
760798

761799
// Sorting by latency this should result in rid: 5, 2, 1, 3
762800
// rid 4 with latency 0 should be at the end

net/sockstats/sockstats_tsgo.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,13 @@ func setNetMon(netMon *netmon.Monitor) {
279279
if ifName == "" {
280280
return
281281
}
282-
ifIndex := state.Interface[ifName].Index
282+
// DefaultRouteInterface and Interface are gathered at different points in time.
283+
// Check for existence first, to avoid a nil pointer dereference.
284+
iface, ok := state.Interface[ifName]
285+
if !ok {
286+
return
287+
}
288+
ifIndex := iface.Index
283289
sockStats.mu.Lock()
284290
defer sockStats.mu.Unlock()
285291
// Ignore changes to unknown interfaces -- it would require

wgengine/magicsock/derp.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ func (c *Conn) maybeSetNearestDERP(report *netcheck.Report) (preferredDERP int)
158158
} else {
159159
connectedToControl = c.health.GetInPollNetMap()
160160
}
161+
c.mu.Lock()
162+
myDerp := c.myDerp
163+
c.mu.Unlock()
161164
if !connectedToControl {
162-
c.mu.Lock()
163-
myDerp := c.myDerp
164-
c.mu.Unlock()
165165
if myDerp != 0 {
166166
metricDERPHomeNoChangeNoControl.Add(1)
167167
return myDerp
@@ -178,6 +178,11 @@ func (c *Conn) maybeSetNearestDERP(report *netcheck.Report) (preferredDERP int)
178178
// one.
179179
preferredDERP = c.pickDERPFallback()
180180
}
181+
if preferredDERP != myDerp {
182+
c.logf(
183+
"magicsock: home DERP changing from derp-%d [%dms] to derp-%d [%dms]",
184+
c.myDerp, report.RegionLatency[myDerp].Milliseconds(), preferredDERP, report.RegionLatency[preferredDERP].Milliseconds())
185+
}
181186
if !c.setNearestDERP(preferredDERP) {
182187
preferredDERP = 0
183188
}

0 commit comments

Comments
 (0)