Skip to content

Commit ebb6b9a

Browse files
committed
fix: plan v4/v6 STUN probing based on relative index
Signed-off-by: Spike Curtis <spike@coder.com>
1 parent 3f0f979 commit ebb6b9a

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

net/netcheck/netcheck.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ func makeProbePlan(dm *tailcfg.DERPMap, ifState *interfaces.State, last *Report)
419419
// Coder: Some regions don't have STUN, so we need to make sure we have probed
420420
// enough STUN regions
421421
numSTUN := 0
422-
for ri, reg := range sortRegions(dm, last) {
422+
for _, reg := range sortRegions(dm, last) {
423423
if numSTUN == numIncrementalRegions {
424424
break
425425
}
@@ -430,14 +430,14 @@ func makeProbePlan(dm *tailcfg.DERPMap, ifState *interfaces.State, last *Report)
430430
// By default, each node only gets one STUN packet sent,
431431
// except the fastest two from the previous round.
432432
tries := 1
433-
isFastestTwo := ri < 2
433+
isFastestTwo := numSTUN < 2
434434

435435
if isFastestTwo {
436436
tries = 2
437437
} else if hadBoth {
438438
// For dual stack machines, make the 3rd & slower nodes alternate
439439
// between.
440-
if ri%2 == 0 {
440+
if numSTUN%2 == 0 {
441441
do4, do6 = true, false
442442
} else {
443443
do4, do6 = false, true
@@ -979,6 +979,9 @@ func (c *Client) GetReport(ctx context.Context, dm *tailcfg.DERPMap) (_ *Report,
979979
}
980980

981981
plan := makeProbePlan(dm, ifState, last)
982+
if len(plan) == 0 {
983+
c.logf("empty probe plan; do we have STUN regions?")
984+
}
982985

983986
// If we're doing a full probe, also check for a captive portal. We
984987
// delay by a bit to wait for UDP STUN to finish, to avoid the probe if

net/netcheck/netcheck_test.go

+114
Original file line numberDiff line numberDiff line change
@@ -1202,3 +1202,117 @@ func TestNeverPickSTUNOnlyRegionAsPreferredDERP(t *testing.T) {
12021202
t.Errorf("got PreferredDERP=%d; want 1", r.PreferredDERP)
12031203
}
12041204
}
1205+
1206+
func Test_makeProbePlan_incremental(t *testing.T) {
1207+
type testcase struct {
1208+
name string
1209+
haveV4 bool
1210+
haveV6 bool
1211+
stunPorts []int
1212+
expected []string
1213+
}
1214+
testcases := []testcase{
1215+
{
1216+
name: "slowSTUN",
1217+
haveV4: true,
1218+
stunPorts: []int{-1, -1, -1, 4, 5, 6},
1219+
expected: []string{"region-4-v4", "region-5-v4", "region-6-v4"},
1220+
},
1221+
{
1222+
name: "fastSTUN",
1223+
haveV4: true,
1224+
stunPorts: []int{1, 2, 3, -1, -1, -1},
1225+
expected: []string{"region-1-v4", "region-2-v4", "region-2-v4"},
1226+
},
1227+
{
1228+
name: "dualStackFast",
1229+
haveV4: true,
1230+
haveV6: true,
1231+
stunPorts: []int{1, 2, 3, -1, -1, -1},
1232+
expected: []string{
1233+
"region-1-v4", "region-2-v4", "region-2-v4",
1234+
"region-1-v6", "region-2-v6", // only fastest 2 have v6
1235+
},
1236+
},
1237+
{
1238+
name: "dualStackSlow",
1239+
haveV4: true,
1240+
haveV6: true,
1241+
stunPorts: []int{-1, -1, -1, 4, 5, 6},
1242+
expected: []string{
1243+
"region-4-v4", "region-5-v4", "region-6-v4",
1244+
"region-4-v6", "region-5-v6", // only fastest 2 have v6
1245+
},
1246+
},
1247+
{
1248+
// pathological case: STUN regions are not in top 3 and even
1249+
// indexes re: latency
1250+
name: "dualStackInterspersed",
1251+
haveV4: true,
1252+
haveV6: true,
1253+
stunPorts: []int{-1, -1, -1, 4, -1, 6, -1, 8},
1254+
expected: []string{
1255+
"region-4-v4", "region-6-v4", "region-8-v4",
1256+
"region-4-v6", "region-6-v6", // only fastest 2 have v6
1257+
},
1258+
},
1259+
}
1260+
for _, tc := range testcases {
1261+
tc := tc
1262+
t.Run(tc.name, func(t *testing.T) {
1263+
state := &interfaces.State{
1264+
HaveV4: tc.haveV4,
1265+
HaveV6: tc.haveV6,
1266+
}
1267+
plan := makeProbePlan(
1268+
derpMapBySTUNPort(tc.stunPorts),
1269+
state,
1270+
reportLatencyAscending(len(tc.stunPorts), tc.haveV4, tc.haveV6),
1271+
)
1272+
for _, e := range tc.expected {
1273+
if _, ok := plan[e]; !ok {
1274+
t.Errorf("expected %s in the plan", e)
1275+
}
1276+
}
1277+
})
1278+
}
1279+
1280+
}
1281+
1282+
func derpMapBySTUNPort(ports []int) *tailcfg.DERPMap {
1283+
derpMap := &tailcfg.DERPMap{Regions: make(map[int]*tailcfg.DERPRegion)}
1284+
for i, p := range ports {
1285+
j := i + 1
1286+
var name string
1287+
if p < 0 {
1288+
name = fmt.Sprintf("noSTUN%d", j)
1289+
} else {
1290+
name = fmt.Sprintf("STUN%d", j)
1291+
}
1292+
derpMap.Regions[j] = &tailcfg.DERPRegion{
1293+
RegionID: j,
1294+
RegionCode: name,
1295+
Nodes: []*tailcfg.DERPNode{
1296+
{Name: name, STUNPort: p},
1297+
}}
1298+
}
1299+
return derpMap
1300+
}
1301+
1302+
func reportLatencyAscending(n int, haveV4, haveV6 bool) *Report {
1303+
r := &Report{
1304+
RegionLatency: make(map[int]time.Duration),
1305+
RegionV4Latency: make(map[int]time.Duration),
1306+
RegionV6Latency: make(map[int]time.Duration),
1307+
}
1308+
for i := 1; i <= n; i++ {
1309+
r.RegionLatency[i] = time.Duration(i) * time.Millisecond
1310+
if haveV4 {
1311+
r.RegionV4Latency[i] = time.Duration(i) * time.Millisecond
1312+
}
1313+
if haveV6 {
1314+
r.RegionV6Latency[i] = time.Duration(i) * time.Millisecond
1315+
}
1316+
}
1317+
return r
1318+
}

0 commit comments

Comments
 (0)