-
Notifications
You must be signed in to change notification settings - Fork 130
/
monitor_windows.go
116 lines (98 loc) · 2.59 KB
/
monitor_windows.go
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
package tun
import (
"sync"
"github.com/sagernet/sing-tun/internal/winipcfg"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/common/x/list"
"golang.org/x/sys/windows"
)
type networkUpdateMonitor struct {
routeListener *winipcfg.RouteChangeCallback
interfaceListener *winipcfg.InterfaceChangeCallback
access sync.Mutex
callbacks list.List[NetworkUpdateCallback]
logger logger.Logger
}
func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
return &networkUpdateMonitor{
logger: logger,
}, nil
}
func (m *networkUpdateMonitor) Start() error {
routeListener, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.MibIPforwardRow2) {
m.emit()
})
if err != nil {
return err
}
m.routeListener = routeListener
interfaceListener, err := winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
m.emit()
})
if err != nil {
routeListener.Unregister()
return err
}
m.interfaceListener = interfaceListener
return nil
}
func (m *networkUpdateMonitor) Close() error {
if m.routeListener != nil {
m.routeListener.Unregister()
m.routeListener = nil
}
if m.interfaceListener != nil {
m.interfaceListener.Unregister()
m.interfaceListener = nil
}
return nil
}
func (m *defaultInterfaceMonitor) checkUpdate() error {
rows, err := winipcfg.GetIPForwardTable2(windows.AF_INET)
if err != nil {
return err
}
lowestMetric := ^uint32(0)
alias := ""
var index int
for _, row := range rows {
if row.DestinationPrefix.PrefixLength != 0 {
continue
}
ifrow, err := row.InterfaceLUID.Interface()
if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp {
continue
}
if ifrow.Type == winipcfg.IfTypePropVirtual || ifrow.Type == winipcfg.IfTypeSoftwareLoopback {
continue
}
iface, err := row.InterfaceLUID.IPInterface(windows.AF_INET)
if err != nil {
continue
}
if !iface.Connected {
continue
}
metric := row.Metric + iface.Metric
if metric < lowestMetric {
lowestMetric = metric
alias = ifrow.Alias()
index = int(ifrow.InterfaceIndex)
}
}
if alias == "" {
return ErrNoRoute
}
oldInterface := m.defaultInterface.Load()
newInterface, err := m.interfaceFinder.ByIndex(index)
if err != nil {
return E.Cause(err, "find updated interface: ", alias)
}
m.defaultInterface.Store(newInterface)
if oldInterface != nil && !oldInterface.Equals(*newInterface) {
return nil
}
m.emit(newInterface, 0)
return nil
}