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