github.com/MerlinKodo/sing-tun@v0.1.15/monitor_windows.go (about)

     1  package tun
     2  
     3  import (
     4  	"net/netip"
     5  	"sync"
     6  
     7  	"github.com/MerlinKodo/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  }