Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfc09077

Browse files
authored
feat!: add interface report to coder netcheck (#13562)
re:#13327Adds local interfaces to `coder netcheck` and checks their MTUs for potential problems.This is mostly relevant for end-user systems where VPNs are common. We _could_ also add it to coderd healthcheck, but until I see coderd connecting to workspaces over a VPN in the wild, I don't think its worth the UX effort.Netcheck results get the following:``` "interfaces": { "error": null, "severity": "ok", "warnings": null, "dismissed": false, "interfaces": [ { "name": "lo0", "mtu": 16384, "addresses": [ "127.0.0.1/8", "::1/128", "fe80::1/64" ] }, { "name": "en8", "mtu": 1500, "addresses": [ "192.168.50.217/24", "fe80::c13:1a92:3fa5:dd7e/64" ] } ] }```_Technically_ not back compatible if anyone is parsing `coder netcheck` output as JSON, since the original output is now under `"derp"` in the output.
1 parentd0fc81a commitfc09077

File tree

7 files changed

+300
-5
lines changed

7 files changed

+300
-5
lines changed

‎cli/netcheck.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/coder/coder/v2/coderd/healthcheck/derphealth"
1212
"github.com/coder/coder/v2/codersdk"
13+
"github.com/coder/coder/v2/codersdk/healthsdk"
1314
"github.com/coder/coder/v2/codersdk/workspacesdk"
1415
"github.com/coder/serpent"
1516
)
@@ -34,11 +35,21 @@ func (r *RootCmd) netcheck() *serpent.Command {
3435

3536
_,_=fmt.Fprint(inv.Stderr,"Gathering a network report. This may take a few seconds...\n\n")
3637

37-
varreport derphealth.Report
38-
report.Run(ctx,&derphealth.ReportOptions{
38+
varderpReport derphealth.Report
39+
derpReport.Run(ctx,&derphealth.ReportOptions{
3940
DERPMap:connInfo.DERPMap,
4041
})
4142

43+
ifReport,err:=healthsdk.RunInterfacesReport()
44+
iferr!=nil {
45+
returnxerrors.Errorf("failed to run interfaces report: %w",err)
46+
}
47+
48+
report:= healthsdk.ClientNetcheckReport{
49+
DERP:healthsdk.DERPHealthReport(derpReport),
50+
Interfaces:ifReport,
51+
}
52+
4253
raw,err:=json.MarshalIndent(report,""," ")
4354
iferr!=nil {
4455
returnerr

‎cli/netcheck_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ func TestNetcheck(t *testing.T) {
2626

2727
b:=out.Bytes()
2828
t.Log(string(b))
29-
varreport healthsdk.DERPHealthReport
29+
varreport healthsdk.ClientNetcheckReport
3030
require.NoError(t,json.Unmarshal(b,&report))
3131

3232
// We do not assert that the report is healthy, just that
3333
// it has the expected number of reports per region.
34-
require.Len(t,report.Regions,1+1)// 1 built-in region + 1 test-managed STUN region
35-
for_,v:=rangereport.Regions {
34+
require.Len(t,report.DERP.Regions,1+1)// 1 built-in region + 1 test-managed STUN region
35+
for_,v:=rangereport.DERP.Regions {
3636
require.Len(t,v.NodeReports,len(v.Region.Nodes))
3737
}
3838
}

‎coderd/healthcheck/health/model.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const (
4343
CodeProvisionerDaemonsNoProvisionerDaemonsCode=`EPD01`
4444
CodeProvisionerDaemonVersionMismatchCode=`EPD02`
4545
CodeProvisionerDaemonAPIMajorVersionDeprecatedCode=`EPD03`
46+
47+
CodeInterfaceSmallMTU=`EIF01`
4648
)
4749

4850
// Default docs URL

‎codersdk/healthsdk/healthsdk.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,9 @@ type WorkspaceProxyReport struct {
269269
BaseReport
270270
WorkspaceProxies codersdk.RegionsResponse[codersdk.WorkspaceProxy]`json:"workspace_proxies"`
271271
}
272+
273+
// @typescript-ignore ClientNetcheckReport
274+
typeClientNetcheckReportstruct {
275+
DERPDERPHealthReport`json:"derp"`
276+
InterfacesInterfacesReport`json:"interfaces"`
277+
}

‎codersdk/healthsdk/interfaces.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package healthsdk
2+
3+
import (
4+
"net"
5+
6+
"tailscale.com/net/interfaces"
7+
8+
"github.com/coder/coder/v2/coderd/healthcheck/health"
9+
)
10+
11+
// @typescript-ignore InterfacesReport
12+
typeInterfacesReportstruct {
13+
BaseReport
14+
Interfaces []Interface`json:"interfaces"`
15+
}
16+
17+
// @typescript-ignore Interface
18+
typeInterfacestruct {
19+
Namestring`json:"name"`
20+
MTUint`json:"mtu"`
21+
Addresses []string`json:"addresses"`
22+
}
23+
24+
funcRunInterfacesReport() (InterfacesReport,error) {
25+
st,err:=interfaces.GetState()
26+
iferr!=nil {
27+
returnInterfacesReport{},err
28+
}
29+
returngenerateInterfacesReport(st),nil
30+
}
31+
32+
funcgenerateInterfacesReport(st*interfaces.State) (reportInterfacesReport) {
33+
report.Severity=health.SeverityOK
34+
forname,iface:=rangest.Interface {
35+
// macOS has a ton of random interfaces, so to keep things helpful, let's filter out any
36+
// that:
37+
//
38+
// - are not enabled
39+
// - don't have any addresses
40+
// - have only link-local addresses (e.g. fe80:...)
41+
if (iface.Flags&net.FlagUp)==0 {
42+
continue
43+
}
44+
addrs:=st.InterfaceIPs[name]
45+
iflen(addrs)==0 {
46+
continue
47+
}
48+
varrbool
49+
healthIface:=Interface{
50+
Name:iface.Name,
51+
MTU:iface.MTU,
52+
}
53+
for_,addr:=rangeaddrs {
54+
healthIface.Addresses=append(healthIface.Addresses,addr.String())
55+
ifaddr.Addr().IsLinkLocalUnicast()||addr.Addr().IsLinkLocalMulticast() {
56+
continue
57+
}
58+
r=true
59+
}
60+
if!r {
61+
continue
62+
}
63+
report.Interfaces=append(report.Interfaces,healthIface)
64+
ififace.MTU<1378 {
65+
report.Severity=health.SeverityWarning
66+
report.Warnings=append(report.Warnings,
67+
health.Messagef(health.CodeInterfaceSmallMTU,
68+
"network interface %s has MTU %d (less than 1378), which may cause problems with direct connections",iface.Name,iface.MTU),
69+
)
70+
}
71+
}
72+
returnreport
73+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package healthsdk
2+
3+
import (
4+
"net"
5+
"net/netip"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
"golang.org/x/exp/slices"
11+
"tailscale.com/net/interfaces"
12+
13+
"github.com/coder/coder/v2/coderd/healthcheck/health"
14+
)
15+
16+
funcTest_generateInterfacesReport(t*testing.T) {
17+
t.Parallel()
18+
testCases:= []struct {
19+
namestring
20+
state interfaces.State
21+
severity health.Severity
22+
expectedInterfaces []string
23+
expectedWarnings []string
24+
}{
25+
{
26+
name:"Empty",
27+
state: interfaces.State{},
28+
severity:health.SeverityOK,
29+
expectedInterfaces: []string{},
30+
},
31+
{
32+
name:"Normal",
33+
state: interfaces.State{
34+
Interface:map[string]interfaces.Interface{
35+
"en0": {Interface:&net.Interface{
36+
MTU:1500,
37+
Name:"en0",
38+
Flags:net.FlagUp,
39+
}},
40+
"lo0": {Interface:&net.Interface{
41+
MTU:65535,
42+
Name:"lo0",
43+
Flags:net.FlagUp,
44+
}},
45+
},
46+
InterfaceIPs:map[string][]netip.Prefix{
47+
"en0": {
48+
netip.MustParsePrefix("192.168.100.1/24"),
49+
netip.MustParsePrefix("fe80::c13:1a92:3fa5:dd7e/64"),
50+
},
51+
"lo0": {
52+
netip.MustParsePrefix("127.0.0.1/8"),
53+
netip.MustParsePrefix("::1/128"),
54+
netip.MustParsePrefix("fe80::1/64"),
55+
},
56+
},
57+
},
58+
severity:health.SeverityOK,
59+
expectedInterfaces: []string{"en0","lo0"},
60+
},
61+
{
62+
name:"IgnoreDisabled",
63+
state: interfaces.State{
64+
Interface:map[string]interfaces.Interface{
65+
"en0": {Interface:&net.Interface{
66+
MTU:1300,
67+
Name:"en0",
68+
Flags:0,
69+
}},
70+
"lo0": {Interface:&net.Interface{
71+
MTU:65535,
72+
Name:"lo0",
73+
Flags:net.FlagUp,
74+
}},
75+
},
76+
InterfaceIPs:map[string][]netip.Prefix{
77+
"en0": {netip.MustParsePrefix("192.168.100.1/24")},
78+
"lo0": {netip.MustParsePrefix("127.0.0.1/8")},
79+
},
80+
},
81+
severity:health.SeverityOK,
82+
expectedInterfaces: []string{"lo0"},
83+
},
84+
{
85+
name:"IgnoreLinkLocalOnly",
86+
state: interfaces.State{
87+
Interface:map[string]interfaces.Interface{
88+
"en0": {Interface:&net.Interface{
89+
MTU:1300,
90+
Name:"en0",
91+
Flags:net.FlagUp,
92+
}},
93+
"lo0": {Interface:&net.Interface{
94+
MTU:65535,
95+
Name:"lo0",
96+
Flags:net.FlagUp,
97+
}},
98+
},
99+
InterfaceIPs:map[string][]netip.Prefix{
100+
"en0": {netip.MustParsePrefix("fe80::1:1/64")},
101+
"lo0": {netip.MustParsePrefix("127.0.0.1/8")},
102+
},
103+
},
104+
severity:health.SeverityOK,
105+
expectedInterfaces: []string{"lo0"},
106+
},
107+
{
108+
name:"IgnoreNoAddress",
109+
state: interfaces.State{
110+
Interface:map[string]interfaces.Interface{
111+
"en0": {Interface:&net.Interface{
112+
MTU:1300,
113+
Name:"en0",
114+
Flags:net.FlagUp,
115+
}},
116+
"lo0": {Interface:&net.Interface{
117+
MTU:65535,
118+
Name:"lo0",
119+
Flags:net.FlagUp,
120+
}},
121+
},
122+
InterfaceIPs:map[string][]netip.Prefix{
123+
"en0": {},
124+
"lo0": {netip.MustParsePrefix("127.0.0.1/8")},
125+
},
126+
},
127+
severity:health.SeverityOK,
128+
expectedInterfaces: []string{"lo0"},
129+
},
130+
{
131+
name:"SmallMTUTunnel",
132+
state: interfaces.State{
133+
Interface:map[string]interfaces.Interface{
134+
"en0": {Interface:&net.Interface{
135+
MTU:1500,
136+
Name:"en0",
137+
Flags:net.FlagUp,
138+
}},
139+
"lo0": {Interface:&net.Interface{
140+
MTU:65535,
141+
Name:"lo0",
142+
Flags:net.FlagUp,
143+
}},
144+
"tun0": {Interface:&net.Interface{
145+
MTU:1280,
146+
Name:"tun0",
147+
Flags:net.FlagUp,
148+
}},
149+
},
150+
InterfaceIPs:map[string][]netip.Prefix{
151+
"en0": {netip.MustParsePrefix("192.168.100.1/24")},
152+
"tun0": {netip.MustParsePrefix("10.3.55.9/8")},
153+
"lo0": {netip.MustParsePrefix("127.0.0.1/8")},
154+
},
155+
},
156+
severity:health.SeverityWarning,
157+
expectedInterfaces: []string{"en0","lo0","tun0"},
158+
expectedWarnings: []string{"tun0"},
159+
},
160+
}
161+
162+
for_,tc:=rangetestCases {
163+
tc:=tc
164+
t.Run(tc.name,func(t*testing.T) {
165+
t.Parallel()
166+
r:=generateInterfacesReport(&tc.state)
167+
require.Equal(t,tc.severity,r.Severity)
168+
gotInterfaces:= []string{}
169+
for_,i:=ranger.Interfaces {
170+
gotInterfaces=append(gotInterfaces,i.Name)
171+
}
172+
slices.Sort(gotInterfaces)
173+
slices.Sort(tc.expectedInterfaces)
174+
require.Equal(t,tc.expectedInterfaces,gotInterfaces)
175+
176+
require.Len(t,r.Warnings,len(tc.expectedWarnings),
177+
"expected %d warnings, got %d",len(tc.expectedWarnings),len(r.Warnings))
178+
for_,name:=rangetc.expectedWarnings {
179+
found:=false
180+
for_,w:=ranger.Warnings {
181+
ifstrings.Contains(w.String(),name) {
182+
found=true
183+
break
184+
}
185+
}
186+
if!found {
187+
t.Errorf("missing warning for %s",name)
188+
}
189+
}
190+
})
191+
}
192+
}

‎docs/admin/healthcheck.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,17 @@ version of Coder.
328328
>Note: This may be a transient issue if you are currently in the process of
329329
>updating your deployment.
330330
331+
###EIF01
332+
333+
_Interface with Small MTU_
334+
335+
**Problem:** One or more local interfaces have MTU smaller than 1378, which is
336+
the minimum MTU for Coder to establish direct connections without fragmentation.
337+
338+
**Solution:** Since IP fragmentation can be a source of performance problems, we
339+
recommend you disable the interface when using Coder or
340+
[disable direct connections](../../cli#--disable-direct-connections)
341+
331342
##EUNKNOWN
332343

333344
_Unknown Error_

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp