interfaces_darwin_test.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netmon
  4. import (
  5. "io"
  6. "net/netip"
  7. "os/exec"
  8. "testing"
  9. "go4.org/mem"
  10. "tailscale.com/util/lineiter"
  11. "tailscale.com/version"
  12. )
  13. func TestLikelyHomeRouterIPSyscallExec(t *testing.T) {
  14. syscallIP, _, syscallOK := likelyHomeRouterIPBSDFetchRIB()
  15. netstatIP, netstatIf, netstatOK := likelyHomeRouterIPDarwinExec()
  16. if syscallOK != netstatOK || syscallIP != netstatIP {
  17. t.Errorf("syscall() = %v, %v, netstat = %v, %v",
  18. syscallIP, syscallOK,
  19. netstatIP, netstatOK,
  20. )
  21. }
  22. if !syscallOK {
  23. return
  24. }
  25. def, err := defaultRoute()
  26. if err != nil {
  27. t.Errorf("defaultRoute() error: %v", err)
  28. }
  29. if def.InterfaceName != netstatIf {
  30. t.Errorf("syscall default route interface %s differs from netstat %s", def.InterfaceName, netstatIf)
  31. }
  32. }
  33. /*
  34. Parse out 10.0.0.1 and en0 from:
  35. $ netstat -r -n -f inet
  36. Routing tables
  37. Internet:
  38. Destination Gateway Flags Netif Expire
  39. default 10.0.0.1 UGSc en0
  40. default link#14 UCSI utun2
  41. 10/16 link#4 UCS en0 !
  42. 10.0.0.1/32 link#4 UCS en0 !
  43. ...
  44. */
  45. func likelyHomeRouterIPDarwinExec() (ret netip.Addr, netif string, ok bool) {
  46. if version.IsMobile() {
  47. // Don't try to do subprocesses on iOS. Ends up with log spam like:
  48. // kernel: "Sandbox: IPNExtension(86580) deny(1) process-fork"
  49. // This is why we have likelyHomeRouterIPDarwinSyscall.
  50. return ret, "", false
  51. }
  52. cmd := exec.Command("/usr/sbin/netstat", "-r", "-n", "-f", "inet")
  53. stdout, err := cmd.StdoutPipe()
  54. if err != nil {
  55. return
  56. }
  57. if err := cmd.Start(); err != nil {
  58. return
  59. }
  60. defer cmd.Wait()
  61. defer io.Copy(io.Discard, stdout) // clear the pipe to prevent hangs
  62. var f []mem.RO
  63. for lr := range lineiter.Reader(stdout) {
  64. lineb, err := lr.Value()
  65. if err != nil {
  66. break
  67. }
  68. line := mem.B(lineb)
  69. if !mem.Contains(line, mem.S("default")) {
  70. continue
  71. }
  72. f = mem.AppendFields(f[:0], line)
  73. if len(f) < 4 || !f[0].EqualString("default") {
  74. continue
  75. }
  76. ipm, flagsm, netifm := f[1], f[2], f[3]
  77. if !mem.Contains(flagsm, mem.S("G")) {
  78. continue
  79. }
  80. if mem.Contains(flagsm, mem.S("I")) {
  81. continue
  82. }
  83. ip, err := netip.ParseAddr(string(mem.Append(nil, ipm)))
  84. if err == nil && ip.IsPrivate() {
  85. ret = ip
  86. netif = netifm.StringCopy()
  87. // We've found what we're looking for.
  88. break
  89. }
  90. }
  91. return ret, netif, ret.IsValid()
  92. }
  93. func TestFetchRoutingTable(t *testing.T) {
  94. // Issue 1345: this used to be flaky on darwin.
  95. for range 20 {
  96. _, err := fetchRoutingTable()
  97. if err != nil {
  98. t.Fatal(err)
  99. }
  100. }
  101. }
  102. func TestUpdateLastKnownDefaultRouteInterface(t *testing.T) {
  103. // Pick some interface on the machine
  104. interfaces, err := netInterfaces()
  105. if err != nil || len(interfaces) == 0 {
  106. t.Fatalf("netInterfaces() error: %v", err)
  107. }
  108. // Set it as our last known default route interface
  109. ifName := interfaces[0].Name
  110. UpdateLastKnownDefaultRouteInterface(ifName)
  111. // And make sure we can get it back
  112. route, err := OSDefaultRoute()
  113. if err != nil {
  114. t.Fatalf("OSDefaultRoute() error: %v", err)
  115. }
  116. want, got := ifName, route.InterfaceName
  117. if want != got {
  118. t.Errorf("OSDefaultRoute() = %q, want %q", got, want)
  119. }
  120. }