Skip to content

Commit 504c287

Browse files
committed
Remove interface enumeration
Enumerating the interfaces is not free, especially on Windows where every address lookup goes through the IP Helper service, so we sample slowly. Losing a minute before rotating our client key after a network change is harmless; burning CPU cycles every few seconds on an idle machine is not.
1 parent c3ba78f commit 504c287

2 files changed

Lines changed: 73 additions & 310 deletions

File tree

dnscrypt-proxy/netmon.go

Lines changed: 8 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ import (
1414
"github.com/jedisct1/dlog"
1515
)
1616

17-
const (
18-
defaultNetworkMonitorInterval = 5 * time.Second
19-
maxNetworkMonitorBackoff = 30 * time.Second
20-
offlineNetworkFingerprint = "offline"
21-
)
17+
const defaultNetworkMonitorInterval = 60 * time.Second
2218

2319
type networkInterfaceSnapshot struct {
2420
Name string
@@ -29,35 +25,17 @@ type networkInterfaceSnapshot struct {
2925
Addrs []*net.IPNet
3026
}
3127

32-
type networkMonitorProbeState struct {
33-
address string
34-
retryAt time.Time
35-
backoff time.Duration
36-
}
37-
3828
type networkMonitor struct {
3929
epochValue atomic.Uint64
4030
sampling atomic.Bool
4131
initialized bool
4232
lastInterfaces string
43-
lastRoutes string
44-
probes [2]networkMonitorProbeState
45-
now func() time.Time
4633
interfaces func() ([]networkInterfaceSnapshot, error)
47-
probe func(string) (net.IP, error)
4834
onChange func()
4935
}
5036

5137
func newNetworkMonitor() *networkMonitor {
52-
return &networkMonitor{
53-
probes: [2]networkMonitorProbeState{
54-
{address: "192.0.2.1:9"},
55-
{address: "[2001:db8::1]:9"},
56-
},
57-
now: time.Now,
58-
interfaces: snapshotNetworkInterfaces,
59-
probe: probeNetworkMonitorLocalIP,
60-
}
38+
return &networkMonitor{interfaces: snapshotNetworkInterfaces}
6139
}
6240

6341
func (monitor *networkMonitor) epoch() uint64 {
@@ -120,27 +98,13 @@ func (monitor *networkMonitor) check() {
12098

12199
func (monitor *networkMonitor) sample(notify bool) {
122100
interfaces, err := monitor.interfaces()
123-
interfacesChanged := false
124-
if err == nil {
125-
interfaceFingerprint := buildNetworkInterfaceFingerprint(interfaces)
126-
interfacesChanged = monitor.lastInterfaces != "" && monitor.lastInterfaces != interfaceFingerprint
127-
monitor.lastInterfaces = interfaceFingerprint
128-
if interfacesChanged {
129-
for i := range monitor.probes {
130-
monitor.probes[i].retryAt = time.Time{}
131-
monitor.probes[i].backoff = 0
132-
}
133-
if notify {
134-
monitor.notifyNetworkChange()
135-
}
136-
}
101+
if err != nil {
102+
return
137103
}
138-
139-
localIPs := monitor.discoverLocalIPs(monitor.now())
140-
routeFingerprint := buildNetworkFingerprint(localIPs)
141-
routesChanged := monitor.lastRoutes != "" && monitor.lastRoutes != routeFingerprint
142-
monitor.lastRoutes = routeFingerprint
143-
if notify && routesChanged && !interfacesChanged {
104+
fingerprint := buildNetworkInterfaceFingerprint(interfaces)
105+
changed := monitor.lastInterfaces != "" && monitor.lastInterfaces != fingerprint
106+
monitor.lastInterfaces = fingerprint
107+
if changed && notify {
144108
monitor.notifyNetworkChange()
145109
}
146110
}
@@ -153,53 +117,6 @@ func (monitor *networkMonitor) notifyNetworkChange() {
153117
}
154118
}
155119

156-
func (monitor *networkMonitor) discoverLocalIPs(now time.Time) []net.IP {
157-
localIPs := make([]net.IP, 0, len(monitor.probes))
158-
seen := make(map[string]struct{}, len(monitor.probes))
159-
for i := range monitor.probes {
160-
state := &monitor.probes[i]
161-
if !state.retryAt.IsZero() && now.Before(state.retryAt) {
162-
continue
163-
}
164-
ip, err := monitor.probe(state.address)
165-
if err != nil {
166-
if state.backoff == 0 {
167-
state.backoff = defaultNetworkMonitorInterval
168-
} else {
169-
state.backoff = min(state.backoff*2, maxNetworkMonitorBackoff)
170-
}
171-
state.retryAt = now.Add(state.backoff)
172-
continue
173-
}
174-
state.retryAt = time.Time{}
175-
state.backoff = 0
176-
if ip == nil || ip.IsUnspecified() {
177-
continue
178-
}
179-
ip = append(net.IP(nil), ip...)
180-
key := ip.String()
181-
if _, ok := seen[key]; ok {
182-
continue
183-
}
184-
seen[key] = struct{}{}
185-
localIPs = append(localIPs, ip)
186-
}
187-
return localIPs
188-
}
189-
190-
func probeNetworkMonitorLocalIP(address string) (net.IP, error) {
191-
conn, err := net.DialTimeout("udp", address, time.Second)
192-
if err != nil {
193-
return nil, err
194-
}
195-
defer conn.Close()
196-
localAddr, ok := conn.LocalAddr().(*net.UDPAddr)
197-
if !ok || localAddr.IP == nil || localAddr.IP.IsUnspecified() {
198-
return nil, net.InvalidAddrError("missing local IP")
199-
}
200-
return append(net.IP(nil), localAddr.IP...), nil
201-
}
202-
203120
func snapshotNetworkInterfaces() ([]networkInterfaceSnapshot, error) {
204121
interfaces, err := net.Interfaces()
205122
if err != nil {
@@ -299,24 +216,6 @@ func buildNetworkInterfaceFingerprint(interfaces []networkInterfaceSnapshot) str
299216
return hashNetworkFingerprint(parts)
300217
}
301218

302-
func buildNetworkFingerprint(localIPs []net.IP) string {
303-
if len(localIPs) == 0 {
304-
return offlineNetworkFingerprint
305-
}
306-
parts := make([]string, 0, len(localIPs))
307-
for _, ip := range localIPs {
308-
if ip == nil || ip.IsUnspecified() {
309-
continue
310-
}
311-
ip = append(net.IP(nil), ip...)
312-
parts = append(parts, "ip="+ip.String())
313-
}
314-
if len(parts) == 0 {
315-
return offlineNetworkFingerprint
316-
}
317-
return hashNetworkFingerprint(parts)
318-
}
319-
320219
func hashNetworkFingerprint(parts []string) string {
321220
sort.Strings(parts)
322221
h := sha256.New()

0 commit comments

Comments
 (0)