github.com/sagernet/sing-tun@v0.3.0-beta.5/monitor_windows.go (about)

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