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