netcheck_test.go 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netcheck
  4. import (
  5. "bytes"
  6. "context"
  7. "fmt"
  8. "maps"
  9. "net"
  10. "net/http"
  11. "net/netip"
  12. "reflect"
  13. "slices"
  14. "strconv"
  15. "strings"
  16. "testing"
  17. "time"
  18. "tailscale.com/derp"
  19. "tailscale.com/net/netmon"
  20. "tailscale.com/net/stun/stuntest"
  21. "tailscale.com/tailcfg"
  22. "tailscale.com/tstest/nettest"
  23. )
  24. func newTestClient(t testing.TB) *Client {
  25. c := &Client{
  26. NetMon: netmon.NewStatic(),
  27. Logf: t.Logf,
  28. TimeNow: func() time.Time {
  29. return time.Unix(1729624521, 0)
  30. },
  31. }
  32. return c
  33. }
  34. func TestBasic(t *testing.T) {
  35. stunAddr, cleanup := stuntest.Serve(t)
  36. defer cleanup()
  37. c := newTestClient(t)
  38. ctx, cancel := context.WithCancel(context.Background())
  39. defer cancel()
  40. if err := c.Standalone(ctx, "127.0.0.1:0"); err != nil {
  41. t.Fatal(err)
  42. }
  43. r, err := c.GetReport(ctx, stuntest.DERPMapOf(stunAddr.String()), nil)
  44. if err != nil {
  45. t.Fatal(err)
  46. }
  47. if !r.UDP {
  48. t.Error("want UDP")
  49. }
  50. if r.Now.IsZero() {
  51. t.Error("Now is zero")
  52. }
  53. if len(r.RegionLatency) != 1 {
  54. t.Errorf("expected 1 key in DERPLatency; got %+v", r.RegionLatency)
  55. }
  56. if _, ok := r.RegionLatency[1]; !ok {
  57. t.Errorf("expected key 1 in DERPLatency; got %+v", r.RegionLatency)
  58. }
  59. if !r.GlobalV4.IsValid() {
  60. t.Error("expected GlobalV4 set")
  61. }
  62. if r.PreferredDERP != 1 {
  63. t.Errorf("PreferredDERP = %v; want 1", r.PreferredDERP)
  64. }
  65. v4Addrs, _ := r.GetGlobalAddrs()
  66. if len(v4Addrs) != 1 {
  67. t.Error("expected one global IPv4 address")
  68. }
  69. if got, want := v4Addrs[0], r.GlobalV4; got != want {
  70. t.Errorf("got %v; want %v", got, want)
  71. }
  72. }
  73. func TestMultiGlobalAddressMapping(t *testing.T) {
  74. c := &Client{
  75. Logf: t.Logf,
  76. }
  77. rs := &reportState{
  78. c: c,
  79. start: time.Now(),
  80. report: newReport(),
  81. }
  82. derpNode := &tailcfg.DERPNode{}
  83. port1 := netip.MustParseAddrPort("127.0.0.1:1234")
  84. port2 := netip.MustParseAddrPort("127.0.0.1:2345")
  85. port3 := netip.MustParseAddrPort("127.0.0.1:3456")
  86. // First report for port1
  87. rs.addNodeLatency(derpNode, port1, 10*time.Millisecond)
  88. // Singular report for port2
  89. rs.addNodeLatency(derpNode, port2, 11*time.Millisecond)
  90. // Duplicate reports for port3
  91. rs.addNodeLatency(derpNode, port3, 12*time.Millisecond)
  92. rs.addNodeLatency(derpNode, port3, 13*time.Millisecond)
  93. r := rs.report
  94. v4Addrs, _ := r.GetGlobalAddrs()
  95. wantV4Addrs := []netip.AddrPort{port1, port3}
  96. if !slices.Equal(v4Addrs, wantV4Addrs) {
  97. t.Errorf("got global addresses: %v, want %v", v4Addrs, wantV4Addrs)
  98. }
  99. }
  100. func TestWorksWhenUDPBlocked(t *testing.T) {
  101. blackhole, err := net.ListenPacket("udp4", "127.0.0.1:0")
  102. if err != nil {
  103. t.Fatalf("failed to open blackhole STUN listener: %v", err)
  104. }
  105. defer blackhole.Close()
  106. stunAddr := blackhole.LocalAddr().String()
  107. dm := stuntest.DERPMapOf(stunAddr)
  108. dm.Regions[1].Nodes[0].STUNOnly = true
  109. c := newTestClient(t)
  110. ctx, cancel := context.WithCancel(context.Background())
  111. defer cancel()
  112. r, err := c.GetReport(ctx, dm, nil)
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. r.UPnP = ""
  117. r.PMP = ""
  118. r.PCP = ""
  119. want := newReport()
  120. // The Now field can't be compared with reflect.DeepEqual; check using
  121. // the Equal method and then overwrite it so that the comparison below
  122. // succeeds.
  123. if !r.Now.Equal(c.TimeNow()) {
  124. t.Errorf("Now = %v; want %v", r.Now, c.TimeNow())
  125. }
  126. want.Now = r.Now
  127. // The IPv4CanSend flag gets set differently across platforms.
  128. // On Windows this test detects false, while on Linux detects true.
  129. // That's not relevant to this test, so just accept what we're
  130. // given.
  131. want.IPv4CanSend = r.IPv4CanSend
  132. // OS IPv6 test is irrelevant here, accept whatever the current
  133. // machine has.
  134. want.OSHasIPv6 = r.OSHasIPv6
  135. // Captive portal test is irrelevant; accept what the current report
  136. // has.
  137. want.CaptivePortal = r.CaptivePortal
  138. if !reflect.DeepEqual(r, want) {
  139. t.Errorf("mismatch\n got: %+v\nwant: %+v\n", r, want)
  140. }
  141. }
  142. func TestAddReportHistoryAndSetPreferredDERP(t *testing.T) {
  143. // report returns a *Report from (DERP host, time.Duration)+ pairs.
  144. report := func(a ...any) *Report {
  145. r := &Report{RegionLatency: map[int]time.Duration{}}
  146. for i := 0; i < len(a); i += 2 {
  147. s := a[i].(string)
  148. if !strings.HasPrefix(s, "d") {
  149. t.Fatalf("invalid derp server key %q", s)
  150. }
  151. regionID, err := strconv.Atoi(s[1:])
  152. if err != nil {
  153. t.Fatalf("invalid derp server key %q", s)
  154. }
  155. switch v := a[i+1].(type) {
  156. case time.Duration:
  157. r.RegionLatency[regionID] = v
  158. case int:
  159. r.RegionLatency[regionID] = time.Second * time.Duration(v)
  160. default:
  161. panic(fmt.Sprintf("unexpected type %T", v))
  162. }
  163. }
  164. return r
  165. }
  166. mkLDAFunc := func(mm map[int]time.Time) func(int) time.Time {
  167. return func(region int) time.Time {
  168. return mm[region]
  169. }
  170. }
  171. type step struct {
  172. after time.Duration
  173. r *Report
  174. }
  175. startTime := time.Unix(123, 0)
  176. tests := []struct {
  177. name string
  178. steps []step
  179. homeParams *tailcfg.DERPHomeParams
  180. opts *GetReportOpts
  181. forcedDERP int // if non-zero, force this DERP to be the preferred one
  182. wantDERP int // want PreferredDERP on final step
  183. wantPrevLen int // wanted len(c.prev)
  184. }{
  185. {
  186. name: "first_reading",
  187. steps: []step{
  188. {0, report("d1", 2, "d2", 3)},
  189. },
  190. wantPrevLen: 1,
  191. wantDERP: 1,
  192. },
  193. {
  194. name: "with_two",
  195. steps: []step{
  196. {0, report("d1", 2, "d2", 3)},
  197. {1 * time.Second, report("d1", 4, "d2", 3)},
  198. },
  199. wantPrevLen: 2,
  200. wantDERP: 1, // t0's d1 of 2 is still best
  201. },
  202. {
  203. name: "but_now_d1_gone",
  204. steps: []step{
  205. {0, report("d1", 2, "d2", 3)},
  206. {1 * time.Second, report("d1", 4, "d2", 3)},
  207. {2 * time.Second, report("d2", 3)},
  208. },
  209. wantPrevLen: 3,
  210. wantDERP: 2, // only option
  211. },
  212. {
  213. name: "d1_is_back",
  214. steps: []step{
  215. {0, report("d1", 2, "d2", 3)},
  216. {1 * time.Second, report("d1", 4, "d2", 3)},
  217. {2 * time.Second, report("d2", 3)},
  218. {3 * time.Second, report("d1", 4, "d2", 3)}, // same as 2 seconds ago
  219. },
  220. wantPrevLen: 4,
  221. wantDERP: 1, // t0's d1 of 2 is still best
  222. },
  223. {
  224. name: "things_clean_up",
  225. steps: []step{
  226. {0, report("d1", 1, "d2", 2)},
  227. {1 * time.Second, report("d1", 1, "d2", 2)},
  228. {2 * time.Second, report("d1", 1, "d2", 2)},
  229. {3 * time.Second, report("d1", 1, "d2", 2)},
  230. {10 * time.Minute, report("d3", 3)},
  231. },
  232. wantPrevLen: 1, // t=[0123]s all gone. (too old, older than 10 min)
  233. wantDERP: 3, // only option
  234. },
  235. {
  236. name: "preferred_derp_hysteresis_no_switch",
  237. steps: []step{
  238. {0 * time.Second, report("d1", 4, "d2", 5)},
  239. {1 * time.Second, report("d1", 4, "d2", 3)},
  240. },
  241. wantPrevLen: 2,
  242. wantDERP: 1, // 2 didn't get fast enough
  243. },
  244. {
  245. name: "preferred_derp_hysteresis_no_switch_absolute",
  246. steps: []step{
  247. {0 * time.Second, report("d1", 4*time.Millisecond, "d2", 5*time.Millisecond)},
  248. {1 * time.Second, report("d1", 4*time.Millisecond, "d2", 1*time.Millisecond)},
  249. },
  250. wantPrevLen: 2,
  251. wantDERP: 1, // 2 is 50%+ faster, but the absolute diff is <10ms
  252. },
  253. {
  254. name: "preferred_derp_hysteresis_do_switch",
  255. steps: []step{
  256. {0 * time.Second, report("d1", 4, "d2", 5)},
  257. {1 * time.Second, report("d1", 4, "d2", 1)},
  258. },
  259. wantPrevLen: 2,
  260. wantDERP: 2, // 2 got fast enough
  261. },
  262. {
  263. name: "derp_home_params",
  264. homeParams: &tailcfg.DERPHomeParams{
  265. RegionScore: map[int]float64{
  266. 1: 2.0 / 3, // 66%
  267. },
  268. },
  269. steps: []step{
  270. // We only use a single step here to avoid
  271. // conflating DERP selection as a result of
  272. // weight hints with the "stickiness" check
  273. // that tries to not change the home DERP
  274. // between steps.
  275. {1 * time.Second, report("d1", 10, "d2", 8)},
  276. },
  277. wantPrevLen: 1,
  278. wantDERP: 1, // 2 was faster, but not by 50%+
  279. },
  280. {
  281. name: "derp_home_params_high_latency",
  282. homeParams: &tailcfg.DERPHomeParams{
  283. RegionScore: map[int]float64{
  284. 1: 2.0 / 3, // 66%
  285. },
  286. },
  287. steps: []step{
  288. // See derp_home_params for why this is a single step.
  289. {1 * time.Second, report("d1", 100, "d2", 10)},
  290. },
  291. wantPrevLen: 1,
  292. wantDERP: 2, // 2 was faster by more than 50%
  293. },
  294. {
  295. name: "derp_home_params_invalid",
  296. homeParams: &tailcfg.DERPHomeParams{
  297. RegionScore: map[int]float64{
  298. 1: 0.0,
  299. 2: -1.0,
  300. },
  301. },
  302. steps: []step{
  303. {1 * time.Second, report("d1", 4, "d2", 5)},
  304. },
  305. wantPrevLen: 1,
  306. wantDERP: 1,
  307. },
  308. {
  309. name: "saw_derp_traffic",
  310. steps: []step{
  311. {0, report("d1", 2, "d2", 3)}, // (1) initially pick d1
  312. {2 * time.Second, report("d1", 4, "d2", 3)}, // (2) still d1
  313. {2 * time.Second, report("d2", 3)}, // (3) d1 gone, but have traffic
  314. },
  315. opts: &GetReportOpts{
  316. GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
  317. 1: startTime.Add(2*time.Second + PreferredDERPFrameTime/2), // within active window of step (3)
  318. }),
  319. },
  320. wantPrevLen: 3,
  321. wantDERP: 1, // still on 1 since we got traffic from it
  322. },
  323. {
  324. name: "saw_derp_traffic_history",
  325. steps: []step{
  326. {0, report("d1", 2, "d2", 3)}, // (1) initially pick d1
  327. {2 * time.Second, report("d1", 4, "d2", 3)}, // (2) still d1
  328. {2 * time.Second, report("d2", 3)}, // (3) d1 gone, but have traffic
  329. },
  330. opts: &GetReportOpts{
  331. GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
  332. 1: startTime.Add(4*time.Second - PreferredDERPFrameTime - 1), // not within active window of (3)
  333. }),
  334. },
  335. wantPrevLen: 3,
  336. wantDERP: 2, // moved to d2 since d1 is gone
  337. },
  338. {
  339. name: "preferred_derp_hysteresis_no_switch_pct",
  340. steps: []step{
  341. {0 * time.Second, report("d1", 34*time.Millisecond, "d2", 35*time.Millisecond)},
  342. {1 * time.Second, report("d1", 34*time.Millisecond, "d2", 23*time.Millisecond)},
  343. },
  344. wantPrevLen: 2,
  345. wantDERP: 1, // diff is 11ms, but d2 is greater than 2/3s of d1
  346. },
  347. {
  348. name: "forced_two",
  349. steps: []step{
  350. {time.Second, report("d1", 2, "d2", 3)},
  351. {2 * time.Second, report("d1", 4, "d2", 3)},
  352. },
  353. forcedDERP: 2,
  354. wantPrevLen: 2,
  355. wantDERP: 2,
  356. },
  357. {
  358. name: "forced_two_unavailable",
  359. steps: []step{
  360. {time.Second, report("d1", 2, "d2", 1)},
  361. {2 * time.Second, report("d1", 4)},
  362. },
  363. forcedDERP: 2,
  364. wantPrevLen: 2,
  365. wantDERP: 1,
  366. },
  367. {
  368. name: "forced_two_no_probe_recent_activity",
  369. steps: []step{
  370. {time.Second, report("d1", 2)},
  371. {2 * time.Second, report("d1", 4)},
  372. },
  373. opts: &GetReportOpts{
  374. GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
  375. 1: startTime,
  376. 2: startTime.Add(time.Second),
  377. }),
  378. },
  379. forcedDERP: 2,
  380. wantPrevLen: 2,
  381. wantDERP: 2,
  382. },
  383. {
  384. name: "forced_two_no_probe_no_recent_activity",
  385. steps: []step{
  386. {time.Second, report("d1", 2)},
  387. {PreferredDERPFrameTime + time.Second, report("d1", 4)},
  388. },
  389. opts: &GetReportOpts{
  390. GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
  391. 1: startTime,
  392. 2: startTime,
  393. }),
  394. },
  395. forcedDERP: 2,
  396. wantPrevLen: 2,
  397. wantDERP: 1,
  398. },
  399. {
  400. name: "no_data_keep_home",
  401. steps: []step{
  402. {0, report("d1", 2, "d2", 3)},
  403. {30 * time.Second, report()},
  404. {2 * time.Second, report()},
  405. {2 * time.Second, report()},
  406. {2 * time.Second, report()},
  407. {2 * time.Second, report()},
  408. },
  409. opts: &GetReportOpts{
  410. GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
  411. 1: startTime,
  412. }),
  413. },
  414. wantPrevLen: 6,
  415. wantDERP: 1,
  416. },
  417. {
  418. name: "no_data_home_expires",
  419. steps: []step{
  420. {0, report("d1", 2, "d2", 3)},
  421. {30 * time.Second, report()},
  422. {2 * derp.KeepAlive, report()},
  423. },
  424. opts: &GetReportOpts{
  425. GetLastDERPActivity: mkLDAFunc(map[int]time.Time{
  426. 1: startTime,
  427. }),
  428. },
  429. wantPrevLen: 3,
  430. wantDERP: 0,
  431. },
  432. }
  433. for _, tt := range tests {
  434. t.Run(tt.name, func(t *testing.T) {
  435. fakeTime := startTime
  436. c := &Client{
  437. TimeNow: func() time.Time { return fakeTime },
  438. ForcePreferredDERP: tt.forcedDERP,
  439. }
  440. dm := &tailcfg.DERPMap{HomeParams: tt.homeParams}
  441. rs := &reportState{
  442. c: c,
  443. start: fakeTime,
  444. opts: tt.opts,
  445. }
  446. for _, s := range tt.steps {
  447. fakeTime = fakeTime.Add(s.after)
  448. rs.start = fakeTime.Add(-100 * time.Millisecond)
  449. c.addReportHistoryAndSetPreferredDERP(rs, s.r, dm.View())
  450. }
  451. lastReport := tt.steps[len(tt.steps)-1].r
  452. if got, want := len(c.prev), tt.wantPrevLen; got != want {
  453. t.Errorf("len(prev) = %v; want %v", got, want)
  454. }
  455. if got, want := lastReport.PreferredDERP, tt.wantDERP; got != want {
  456. t.Errorf("PreferredDERP = %v; want %v", got, want)
  457. }
  458. })
  459. }
  460. }
  461. func TestMakeProbePlan(t *testing.T) {
  462. // basicMap has 5 regions. each region has a number of nodes
  463. // equal to the region number (1 has 1a, 2 has 2a and 2b, etc.)
  464. basicMap := &tailcfg.DERPMap{
  465. Regions: map[int]*tailcfg.DERPRegion{},
  466. }
  467. for rid := 1; rid <= 6; rid++ {
  468. var nodes []*tailcfg.DERPNode
  469. for nid := 0; nid < rid; nid++ {
  470. nodes = append(nodes, &tailcfg.DERPNode{
  471. Name: fmt.Sprintf("%d%c", rid, 'a'+rune(nid)),
  472. RegionID: rid,
  473. HostName: fmt.Sprintf("derp%d-%d", rid, nid),
  474. IPv4: fmt.Sprintf("%d.0.0.%d", rid, nid),
  475. IPv6: fmt.Sprintf("%d::%d", rid, nid),
  476. })
  477. }
  478. basicMap.Regions[rid] = &tailcfg.DERPRegion{
  479. RegionID: rid,
  480. Nodes: nodes,
  481. NoMeasureNoHome: rid == 6,
  482. }
  483. }
  484. const ms = time.Millisecond
  485. p := func(name string, c rune, d ...time.Duration) probe {
  486. var proto probeProto
  487. switch c {
  488. case 4:
  489. proto = probeIPv4
  490. case 6:
  491. proto = probeIPv6
  492. case 'h':
  493. proto = probeHTTPS
  494. }
  495. pr := probe{node: name, proto: proto}
  496. if len(d) == 1 {
  497. pr.delay = d[0]
  498. } else if len(d) > 1 {
  499. panic("too many args")
  500. }
  501. return pr
  502. }
  503. tests := []struct {
  504. name string
  505. dm *tailcfg.DERPMap
  506. have6if bool
  507. no4 bool // no IPv4
  508. last *Report
  509. want probePlan
  510. }{
  511. {
  512. name: "initial_v6",
  513. dm: basicMap,
  514. have6if: true,
  515. last: nil, // initial
  516. want: probePlan{
  517. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 100*ms), p("1a", 4, 200*ms)}, // all a
  518. "region-1-v6": []probe{p("1a", 6), p("1a", 6, 100*ms), p("1a", 6, 200*ms)},
  519. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 100*ms), p("2a", 4, 200*ms)}, // a -> b -> a
  520. "region-2-v6": []probe{p("2a", 6), p("2b", 6, 100*ms), p("2a", 6, 200*ms)},
  521. "region-3-v4": []probe{p("3a", 4), p("3b", 4, 100*ms), p("3c", 4, 200*ms)}, // a -> b -> c
  522. "region-3-v6": []probe{p("3a", 6), p("3b", 6, 100*ms), p("3c", 6, 200*ms)},
  523. "region-4-v4": []probe{p("4a", 4), p("4b", 4, 100*ms), p("4c", 4, 200*ms)},
  524. "region-4-v6": []probe{p("4a", 6), p("4b", 6, 100*ms), p("4c", 6, 200*ms)},
  525. "region-5-v4": []probe{p("5a", 4), p("5b", 4, 100*ms), p("5c", 4, 200*ms)},
  526. "region-5-v6": []probe{p("5a", 6), p("5b", 6, 100*ms), p("5c", 6, 200*ms)},
  527. },
  528. },
  529. {
  530. name: "initial_no_v6",
  531. dm: basicMap,
  532. have6if: false,
  533. last: nil, // initial
  534. want: probePlan{
  535. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 100*ms), p("1a", 4, 200*ms)}, // all a
  536. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 100*ms), p("2a", 4, 200*ms)}, // a -> b -> a
  537. "region-3-v4": []probe{p("3a", 4), p("3b", 4, 100*ms), p("3c", 4, 200*ms)}, // a -> b -> c
  538. "region-4-v4": []probe{p("4a", 4), p("4b", 4, 100*ms), p("4c", 4, 200*ms)},
  539. "region-5-v4": []probe{p("5a", 4), p("5b", 4, 100*ms), p("5c", 4, 200*ms)},
  540. },
  541. },
  542. {
  543. name: "second_v4_no_6if",
  544. dm: basicMap,
  545. have6if: false,
  546. last: &Report{
  547. RegionLatency: map[int]time.Duration{
  548. 1: 10 * time.Millisecond,
  549. 2: 20 * time.Millisecond,
  550. 3: 30 * time.Millisecond,
  551. 4: 40 * time.Millisecond,
  552. // Pretend 5 is missing
  553. },
  554. RegionV4Latency: map[int]time.Duration{
  555. 1: 10 * time.Millisecond,
  556. 2: 20 * time.Millisecond,
  557. 3: 30 * time.Millisecond,
  558. 4: 40 * time.Millisecond,
  559. },
  560. },
  561. want: probePlan{
  562. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 12*ms)},
  563. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 24*ms)},
  564. "region-3-v4": []probe{p("3a", 4)},
  565. },
  566. },
  567. {
  568. name: "second_v4_only_with_6if",
  569. dm: basicMap,
  570. have6if: true,
  571. last: &Report{
  572. RegionLatency: map[int]time.Duration{
  573. 1: 10 * time.Millisecond,
  574. 2: 20 * time.Millisecond,
  575. 3: 30 * time.Millisecond,
  576. 4: 40 * time.Millisecond,
  577. // Pretend 5 is missing
  578. },
  579. RegionV4Latency: map[int]time.Duration{
  580. 1: 10 * time.Millisecond,
  581. 2: 20 * time.Millisecond,
  582. 3: 30 * time.Millisecond,
  583. 4: 40 * time.Millisecond,
  584. },
  585. },
  586. want: probePlan{
  587. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 12*ms)},
  588. "region-1-v6": []probe{p("1a", 6)},
  589. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 24*ms)},
  590. "region-2-v6": []probe{p("2a", 6)},
  591. "region-3-v4": []probe{p("3a", 4)},
  592. },
  593. },
  594. {
  595. name: "second_mixed",
  596. dm: basicMap,
  597. have6if: true,
  598. last: &Report{
  599. RegionLatency: map[int]time.Duration{
  600. 1: 10 * time.Millisecond,
  601. 2: 20 * time.Millisecond,
  602. 3: 30 * time.Millisecond,
  603. 4: 40 * time.Millisecond,
  604. // Pretend 5 is missing
  605. },
  606. RegionV4Latency: map[int]time.Duration{
  607. 1: 10 * time.Millisecond,
  608. 2: 20 * time.Millisecond,
  609. },
  610. RegionV6Latency: map[int]time.Duration{
  611. 3: 30 * time.Millisecond,
  612. 4: 40 * time.Millisecond,
  613. },
  614. },
  615. want: probePlan{
  616. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 12*ms)},
  617. "region-1-v6": []probe{p("1a", 6), p("1a", 6, 12*ms)},
  618. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 24*ms)},
  619. "region-2-v6": []probe{p("2a", 6), p("2b", 6, 24*ms)},
  620. "region-3-v4": []probe{p("3a", 4)},
  621. },
  622. },
  623. {
  624. name: "only_v6_initial",
  625. have6if: true,
  626. no4: true,
  627. dm: basicMap,
  628. want: probePlan{
  629. "region-1-v6": []probe{p("1a", 6), p("1a", 6, 100*ms), p("1a", 6, 200*ms)},
  630. "region-2-v6": []probe{p("2a", 6), p("2b", 6, 100*ms), p("2a", 6, 200*ms)},
  631. "region-3-v6": []probe{p("3a", 6), p("3b", 6, 100*ms), p("3c", 6, 200*ms)},
  632. "region-4-v6": []probe{p("4a", 6), p("4b", 6, 100*ms), p("4c", 6, 200*ms)},
  633. "region-5-v6": []probe{p("5a", 6), p("5b", 6, 100*ms), p("5c", 6, 200*ms)},
  634. },
  635. },
  636. {
  637. name: "try_harder_for_preferred_derp",
  638. dm: basicMap,
  639. have6if: true,
  640. last: &Report{
  641. RegionLatency: map[int]time.Duration{
  642. 1: 10 * time.Millisecond,
  643. 2: 20 * time.Millisecond,
  644. 3: 30 * time.Millisecond,
  645. 4: 40 * time.Millisecond,
  646. },
  647. RegionV4Latency: map[int]time.Duration{
  648. 1: 10 * time.Millisecond,
  649. 2: 20 * time.Millisecond,
  650. },
  651. RegionV6Latency: map[int]time.Duration{
  652. 3: 30 * time.Millisecond,
  653. 4: 40 * time.Millisecond,
  654. },
  655. PreferredDERP: 1,
  656. },
  657. want: probePlan{
  658. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 12*ms), p("1a", 4, 124*ms), p("1a", 4, 186*ms)},
  659. "region-1-v6": []probe{p("1a", 6), p("1a", 6, 12*ms), p("1a", 6, 124*ms), p("1a", 6, 186*ms)},
  660. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 24*ms)},
  661. "region-2-v6": []probe{p("2a", 6), p("2b", 6, 24*ms)},
  662. "region-3-v4": []probe{p("3a", 4)},
  663. },
  664. },
  665. {
  666. // #13969: ensure that the prior/current home region is always included in
  667. // probe plans, so that we don't flap between regions due to a single major
  668. // netcheck having excluded the home region due to a spuriously high sample.
  669. name: "ensure_home_region_inclusion",
  670. dm: basicMap,
  671. have6if: true,
  672. last: &Report{
  673. RegionLatency: map[int]time.Duration{
  674. 1: 50 * time.Millisecond,
  675. 2: 20 * time.Millisecond,
  676. 3: 30 * time.Millisecond,
  677. 4: 40 * time.Millisecond,
  678. },
  679. RegionV4Latency: map[int]time.Duration{
  680. 1: 50 * time.Millisecond,
  681. 2: 20 * time.Millisecond,
  682. },
  683. RegionV6Latency: map[int]time.Duration{
  684. 3: 30 * time.Millisecond,
  685. 4: 40 * time.Millisecond,
  686. },
  687. PreferredDERP: 1,
  688. },
  689. want: probePlan{
  690. "region-1-v4": []probe{p("1a", 4), p("1a", 4, 60*ms), p("1a", 4, 220*ms), p("1a", 4, 330*ms)},
  691. "region-1-v6": []probe{p("1a", 6), p("1a", 6, 60*ms), p("1a", 6, 220*ms), p("1a", 6, 330*ms)},
  692. "region-2-v4": []probe{p("2a", 4), p("2b", 4, 24*ms)},
  693. "region-2-v6": []probe{p("2a", 6), p("2b", 6, 24*ms)},
  694. "region-3-v4": []probe{p("3a", 4), p("3b", 4, 36*ms)},
  695. "region-3-v6": []probe{p("3a", 6), p("3b", 6, 36*ms)},
  696. "region-4-v4": []probe{p("4a", 4)},
  697. },
  698. },
  699. }
  700. for _, tt := range tests {
  701. t.Run(tt.name, func(t *testing.T) {
  702. ifState := &netmon.State{
  703. HaveV6: tt.have6if,
  704. HaveV4: !tt.no4,
  705. }
  706. preferredDERP := 0
  707. if tt.last != nil {
  708. preferredDERP = tt.last.PreferredDERP
  709. }
  710. got := makeProbePlan(tt.dm, ifState, tt.last, preferredDERP)
  711. if !reflect.DeepEqual(got, tt.want) {
  712. t.Errorf("unexpected plan; got:\n%v\nwant:\n%v\n", got, tt.want)
  713. }
  714. })
  715. }
  716. }
  717. func (plan probePlan) String() string {
  718. var sb strings.Builder
  719. for _, key := range slices.Sorted(maps.Keys(plan)) {
  720. fmt.Fprintf(&sb, "[%s]", key)
  721. pv := plan[key]
  722. for _, p := range pv {
  723. fmt.Fprintf(&sb, " %v", p)
  724. }
  725. sb.WriteByte('\n')
  726. }
  727. return sb.String()
  728. }
  729. func (p probe) String() string {
  730. wait := ""
  731. if p.wait > 0 {
  732. wait = "+" + p.wait.String()
  733. }
  734. delay := ""
  735. if p.delay > 0 {
  736. delay = "@" + p.delay.String()
  737. }
  738. return fmt.Sprintf("%s-%s%s%s", p.node, p.proto, delay, wait)
  739. }
  740. func TestLogConciseReport(t *testing.T) {
  741. dm := &tailcfg.DERPMap{
  742. Regions: map[int]*tailcfg.DERPRegion{
  743. 1: nil,
  744. 2: nil,
  745. 3: nil,
  746. },
  747. }
  748. const ms = time.Millisecond
  749. tests := []struct {
  750. name string
  751. r *Report
  752. want string
  753. }{
  754. {
  755. name: "no_udp",
  756. r: &Report{},
  757. want: "udp=false v4=false icmpv4=false v6=false mapvarydest= portmap=? derp=0",
  758. },
  759. {
  760. name: "no_udp_icmp",
  761. r: &Report{ICMPv4: true, IPv4: true},
  762. want: "udp=false icmpv4=true v6=false mapvarydest= portmap=? derp=0",
  763. },
  764. {
  765. name: "ipv4_one_region",
  766. r: &Report{
  767. UDP: true,
  768. IPv4: true,
  769. PreferredDERP: 1,
  770. RegionLatency: map[int]time.Duration{
  771. 1: 10 * ms,
  772. },
  773. RegionV4Latency: map[int]time.Duration{
  774. 1: 10 * ms,
  775. },
  776. },
  777. want: "udp=true v6=false mapvarydest= portmap=? derp=1 derpdist=1v4:10ms",
  778. },
  779. {
  780. name: "ipv4_all_region",
  781. r: &Report{
  782. UDP: true,
  783. IPv4: true,
  784. PreferredDERP: 1,
  785. RegionLatency: map[int]time.Duration{
  786. 1: 10 * ms,
  787. 2: 20 * ms,
  788. 3: 30 * ms,
  789. },
  790. RegionV4Latency: map[int]time.Duration{
  791. 1: 10 * ms,
  792. 2: 20 * ms,
  793. 3: 30 * ms,
  794. },
  795. },
  796. want: "udp=true v6=false mapvarydest= portmap=? derp=1 derpdist=1v4:10ms,2v4:20ms,3v4:30ms",
  797. },
  798. {
  799. name: "ipboth_all_region",
  800. r: &Report{
  801. UDP: true,
  802. IPv4: true,
  803. IPv6: true,
  804. PreferredDERP: 1,
  805. RegionLatency: map[int]time.Duration{
  806. 1: 10 * ms,
  807. 2: 20 * ms,
  808. 3: 30 * ms,
  809. },
  810. RegionV4Latency: map[int]time.Duration{
  811. 1: 10 * ms,
  812. 2: 20 * ms,
  813. 3: 30 * ms,
  814. },
  815. RegionV6Latency: map[int]time.Duration{
  816. 1: 10 * ms,
  817. 2: 20 * ms,
  818. 3: 30 * ms,
  819. },
  820. },
  821. want: "udp=true v6=true mapvarydest= portmap=? derp=1 derpdist=1v4:10ms,1v6:10ms,2v4:20ms,2v6:20ms,3v4:30ms,3v6:30ms",
  822. },
  823. {
  824. name: "portmap_all",
  825. r: &Report{
  826. UDP: true,
  827. UPnP: "true",
  828. PMP: "true",
  829. PCP: "true",
  830. },
  831. want: "udp=true v4=false v6=false mapvarydest= portmap=UMC derp=0",
  832. },
  833. {
  834. name: "portmap_some",
  835. r: &Report{
  836. UDP: true,
  837. UPnP: "true",
  838. PMP: "false",
  839. PCP: "true",
  840. },
  841. want: "udp=true v4=false v6=false mapvarydest= portmap=UC derp=0",
  842. },
  843. }
  844. for _, tt := range tests {
  845. t.Run(tt.name, func(t *testing.T) {
  846. var buf bytes.Buffer
  847. c := &Client{Logf: func(f string, a ...any) { fmt.Fprintf(&buf, f, a...) }}
  848. c.logConciseReport(tt.r, dm)
  849. if got, ok := strings.CutPrefix(buf.String(), "[v1] report: "); !ok {
  850. t.Errorf("unexpected result.\n got: %#q\nwant: %#q\n", got, tt.want)
  851. }
  852. })
  853. }
  854. }
  855. func TestSortRegions(t *testing.T) {
  856. unsortedMap := &tailcfg.DERPMap{
  857. Regions: map[int]*tailcfg.DERPRegion{},
  858. }
  859. for rid := 1; rid <= 5; rid++ {
  860. var nodes []*tailcfg.DERPNode
  861. nodes = append(nodes, &tailcfg.DERPNode{
  862. Name: fmt.Sprintf("%da", rid),
  863. RegionID: rid,
  864. HostName: fmt.Sprintf("derp%d-1", rid),
  865. IPv4: fmt.Sprintf("%d.0.0.1", rid),
  866. IPv6: fmt.Sprintf("%d::1", rid),
  867. })
  868. unsortedMap.Regions[rid] = &tailcfg.DERPRegion{
  869. RegionID: rid,
  870. Nodes: nodes,
  871. }
  872. }
  873. report := newReport()
  874. report.RegionLatency[1] = time.Second * time.Duration(5)
  875. report.RegionLatency[2] = time.Second * time.Duration(3)
  876. report.RegionLatency[3] = time.Second * time.Duration(6)
  877. report.RegionLatency[4] = time.Second * time.Duration(0)
  878. report.RegionLatency[5] = time.Second * time.Duration(2)
  879. sortedMap := sortRegions(unsortedMap, report, 0)
  880. // Sorting by latency this should result in rid: 5, 2, 1, 3
  881. // rid 4 with latency 0 should be at the end
  882. want := []int{5, 2, 1, 3, 4}
  883. got := make([]int, len(sortedMap))
  884. for i, r := range sortedMap {
  885. got[i] = r.RegionID
  886. }
  887. if !reflect.DeepEqual(got, want) {
  888. t.Errorf("got %v; want %v", got, want)
  889. }
  890. }
  891. type RoundTripFunc func(req *http.Request) *http.Response
  892. func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
  893. return f(req), nil
  894. }
  895. func TestNodeAddrResolve(t *testing.T) {
  896. nettest.SkipIfNoNetwork(t)
  897. c := &Client{
  898. Logf: t.Logf,
  899. UseDNSCache: true,
  900. }
  901. dn := &tailcfg.DERPNode{
  902. Name: "derptest1a",
  903. RegionID: 901,
  904. HostName: "tailscale.com",
  905. // No IPv4 or IPv6 addrs
  906. }
  907. dnV4Only := &tailcfg.DERPNode{
  908. Name: "derptest1b",
  909. RegionID: 901,
  910. HostName: "ipv4.google.com",
  911. // No IPv4 or IPv6 addrs
  912. }
  913. // Checks whether IPv6 and IPv6 DNS resolution works on this platform.
  914. ipv6Works := func(t *testing.T) bool {
  915. // Verify that we can create an IPv6 socket.
  916. ln, err := net.ListenPacket("udp6", "[::1]:0")
  917. if err != nil {
  918. t.Logf("IPv6 may not work on this machine: %v", err)
  919. return false
  920. }
  921. ln.Close()
  922. // Resolve a hostname that we know has an IPv6 address.
  923. addrs, err := net.DefaultResolver.LookupNetIP(context.Background(), "ip6", "google.com")
  924. if err != nil {
  925. t.Logf("IPv6 DNS resolution error: %v", err)
  926. return false
  927. }
  928. if len(addrs) == 0 {
  929. t.Logf("IPv6 DNS resolution returned no addresses")
  930. return false
  931. }
  932. return true
  933. }
  934. ctx := context.Background()
  935. for _, tt := range []bool{true, false} {
  936. t.Run(fmt.Sprintf("UseDNSCache=%v", tt), func(t *testing.T) {
  937. c.resolver = nil
  938. c.UseDNSCache = tt
  939. t.Run("IPv4", func(t *testing.T) {
  940. ap, ok := c.nodeAddrPort(ctx, dn, dn.STUNPort, probeIPv4)
  941. if !ok {
  942. t.Fatal("expected valid AddrPort")
  943. }
  944. if !ap.Addr().Is4() {
  945. t.Fatalf("expected IPv4 addr, got: %v", ap.Addr())
  946. }
  947. t.Logf("got IPv4 addr: %v", ap)
  948. })
  949. t.Run("IPv6", func(t *testing.T) {
  950. // Skip if IPv6 doesn't work on this machine.
  951. if !ipv6Works(t) {
  952. t.Skipf("IPv6 may not work on this machine")
  953. }
  954. ap, ok := c.nodeAddrPort(ctx, dn, dn.STUNPort, probeIPv6)
  955. if !ok {
  956. t.Fatal("expected valid AddrPort")
  957. }
  958. if !ap.Addr().Is6() {
  959. t.Fatalf("expected IPv6 addr, got: %v", ap.Addr())
  960. }
  961. t.Logf("got IPv6 addr: %v", ap)
  962. })
  963. t.Run("IPv6 Failure", func(t *testing.T) {
  964. ap, ok := c.nodeAddrPort(ctx, dnV4Only, dn.STUNPort, probeIPv6)
  965. if ok {
  966. t.Fatalf("expected no addr but got: %v", ap)
  967. }
  968. t.Logf("correctly got invalid addr")
  969. })
  970. })
  971. }
  972. }
  973. func TestReportTimeouts(t *testing.T) {
  974. if ReportTimeout < stunProbeTimeout {
  975. t.Errorf("ReportTimeout (%v) cannot be less than stunProbeTimeout (%v)", ReportTimeout, stunProbeTimeout)
  976. }
  977. if ReportTimeout < icmpProbeTimeout {
  978. t.Errorf("ReportTimeout (%v) cannot be less than icmpProbeTimeout (%v)", ReportTimeout, icmpProbeTimeout)
  979. }
  980. if ReportTimeout < httpsProbeTimeout {
  981. t.Errorf("ReportTimeout (%v) cannot be less than httpsProbeTimeout (%v)", ReportTimeout, httpsProbeTimeout)
  982. }
  983. }
  984. func TestNoUDPNilGetReportOpts(t *testing.T) {
  985. blackhole, err := net.ListenPacket("udp4", "127.0.0.1:0")
  986. if err != nil {
  987. t.Fatalf("failed to open blackhole STUN listener: %v", err)
  988. }
  989. defer blackhole.Close()
  990. dm := stuntest.DERPMapOf(blackhole.LocalAddr().String())
  991. for _, region := range dm.Regions {
  992. for _, n := range region.Nodes {
  993. n.STUNOnly = false // exercise ICMP & HTTPS probing
  994. }
  995. }
  996. c := newTestClient(t)
  997. ctx, cancel := context.WithCancel(context.Background())
  998. defer cancel()
  999. r, err := c.GetReport(ctx, dm, nil)
  1000. if err != nil {
  1001. t.Fatal(err)
  1002. }
  1003. if r.UDP {
  1004. t.Fatal("unexpected working UDP")
  1005. }
  1006. }