github.com/apernet/sing-tun@v0.2.6-0.20240323130332-b9f6511036ad/monitor_windows.go (about) 1 package tun 2 3 import ( 4 "sync" 5 6 "github.com/apernet/sing-tun/internal/winipcfg" 7 E "github.com/sagernet/sing/common/exceptions" 8 "github.com/sagernet/sing/common/logger" 9 "github.com/sagernet/sing/common/x/list" 10 11 "golang.org/x/sys/windows" 12 ) 13 14 type networkUpdateMonitor struct { 15 routeListener *winipcfg.RouteChangeCallback 16 interfaceListener *winipcfg.InterfaceChangeCallback 17 errorHandler E.Handler 18 19 access sync.Mutex 20 callbacks list.List[NetworkUpdateCallback] 21 logger logger.Logger 22 } 23 24 func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) { 25 return &networkUpdateMonitor{ 26 logger: logger, 27 }, nil 28 } 29 30 func (m *networkUpdateMonitor) Start() error { 31 routeListener, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.MibIPforwardRow2) { 32 m.emit() 33 }) 34 if err != nil { 35 return err 36 } 37 m.routeListener = routeListener 38 interfaceListener, err := winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) { 39 m.emit() 40 }) 41 if err != nil { 42 routeListener.Unregister() 43 return err 44 } 45 m.interfaceListener = interfaceListener 46 return nil 47 } 48 49 func (m *networkUpdateMonitor) Close() error { 50 if m.routeListener != nil { 51 m.routeListener.Unregister() 52 m.routeListener = nil 53 } 54 if m.interfaceListener != nil { 55 m.interfaceListener.Unregister() 56 m.interfaceListener = nil 57 } 58 return nil 59 } 60 61 func (m *defaultInterfaceMonitor) checkUpdate() error { 62 rows, err := winipcfg.GetIPForwardTable2(windows.AF_INET) 63 if err != nil { 64 return err 65 } 66 67 lowestMetric := ^uint32(0) 68 alias := "" 69 var index int 70 71 for _, row := range rows { 72 if row.DestinationPrefix.PrefixLength != 0 { 73 continue 74 } 75 76 ifrow, err := row.InterfaceLUID.Interface() 77 if err != nil || ifrow.OperStatus != winipcfg.IfOperStatusUp { 78 continue 79 } 80 81 iface, err := row.InterfaceLUID.IPInterface(windows.AF_INET) 82 if err != nil { 83 continue 84 } 85 86 if ifrow.Type == winipcfg.IfTypePropVirtual || ifrow.Type == winipcfg.IfTypeSoftwareLoopback { 87 continue 88 } 89 90 metric := row.Metric + iface.Metric 91 if metric < lowestMetric { 92 lowestMetric = metric 93 alias = ifrow.Alias() 94 index = int(ifrow.InterfaceIndex) 95 } 96 } 97 98 if alias == "" { 99 return ErrNoRoute 100 } 101 102 oldInterface := m.defaultInterfaceName 103 oldIndex := m.defaultInterfaceIndex 104 105 m.defaultInterfaceName = alias 106 m.defaultInterfaceIndex = index 107 108 if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex { 109 return nil 110 } 111 112 m.emit(EventInterfaceUpdate) 113 return nil 114 }