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

     1  package tun
     2  
     3  import (
     4  	"net"
     5  	"net/netip"
     6  	"os"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/sagernet/sing/common/buf"
    11  	E "github.com/sagernet/sing/common/exceptions"
    12  	"github.com/sagernet/sing/common/logger"
    13  	"github.com/sagernet/sing/common/x/list"
    14  
    15  	"golang.org/x/net/route"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  type networkUpdateMonitor struct {
    20  	access          sync.Mutex
    21  	callbacks       list.List[NetworkUpdateCallback]
    22  	routeSocketFile *os.File
    23  	closeOnce       sync.Once
    24  	done            chan struct{}
    25  	logger          logger.Logger
    26  }
    27  
    28  func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
    29  	return &networkUpdateMonitor{
    30  		logger: logger,
    31  		done:   make(chan struct{}),
    32  	}, nil
    33  }
    34  
    35  func (m *networkUpdateMonitor) Start() error {
    36  	go m.loopUpdate()
    37  	return nil
    38  }
    39  
    40  func (m *networkUpdateMonitor) loopUpdate() {
    41  	for {
    42  		select {
    43  		case <-m.done:
    44  			return
    45  		case <-time.After(time.Second):
    46  		}
    47  		err := m.loopUpdate0()
    48  		if err != nil {
    49  			m.logger.Error("listen network update: ", err)
    50  			return
    51  		}
    52  	}
    53  }
    54  
    55  func (m *networkUpdateMonitor) loopUpdate0() error {
    56  	routeSocket, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0)
    57  	if err != nil {
    58  		return err
    59  	}
    60  	routeSocketFile := os.NewFile(uintptr(routeSocket), "route")
    61  	m.routeSocketFile = routeSocketFile
    62  	m.loopUpdate1(routeSocketFile)
    63  	return nil
    64  }
    65  
    66  func (m *networkUpdateMonitor) loopUpdate1(routeSocketFile *os.File) {
    67  	defer routeSocketFile.Close()
    68  	buffer := buf.NewPacket()
    69  	defer buffer.Release()
    70  	n, err := routeSocketFile.Read(buffer.FreeBytes())
    71  	if err != nil {
    72  		return
    73  	}
    74  	buffer.Truncate(n)
    75  	messages, err := route.ParseRIB(route.RIBTypeRoute, buffer.Bytes())
    76  	if err != nil {
    77  		return
    78  	}
    79  	for _, message := range messages {
    80  		if _, isRouteMessage := message.(*route.RouteMessage); isRouteMessage {
    81  			m.emit()
    82  			return
    83  		}
    84  	}
    85  }
    86  
    87  func (m *networkUpdateMonitor) Close() error {
    88  	m.closeOnce.Do(func() {
    89  		close(m.done)
    90  	})
    91  	return nil
    92  }
    93  
    94  func (m *defaultInterfaceMonitor) checkUpdate() error {
    95  	ribMessage, err := route.FetchRIB(unix.AF_UNSPEC, route.RIBTypeRoute, 0)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	routeMessages, err := route.ParseRIB(route.RIBTypeRoute, ribMessage)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	var defaultInterface *net.Interface
   104  	for _, rawRouteMessage := range routeMessages {
   105  		routeMessage := rawRouteMessage.(*route.RouteMessage)
   106  		if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
   107  			continue
   108  		}
   109  		destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
   110  		if !isIPv4Destination {
   111  			continue
   112  		}
   113  		if destination.IP != netip.IPv4Unspecified().As4() {
   114  			continue
   115  		}
   116  		mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
   117  		if !isIPv4Mask {
   118  			continue
   119  		}
   120  		ones, _ := net.IPMask(mask.IP[:]).Size()
   121  		if ones != 0 {
   122  			continue
   123  		}
   124  		routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
   125  		if err != nil {
   126  			return err
   127  		}
   128  		if routeMessage.Flags&unix.RTF_UP == 0 {
   129  			continue
   130  		}
   131  		if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
   132  			continue
   133  		}
   134  		if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
   135  			// continue
   136  		}
   137  		defaultInterface = routeInterface
   138  		break
   139  	}
   140  	if defaultInterface == nil {
   141  		if m.options.UnderNetworkExtension {
   142  			defaultInterface, err = getDefaultInterfaceBySocket()
   143  			if err != nil {
   144  				return err
   145  			}
   146  		}
   147  	}
   148  	if defaultInterface == nil {
   149  		return ErrNoRoute
   150  	}
   151  	oldInterface := m.defaultInterfaceName
   152  	oldIndex := m.defaultInterfaceIndex
   153  	m.defaultInterfaceIndex = defaultInterface.Index
   154  	m.defaultInterfaceName = defaultInterface.Name
   155  	if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
   156  		return nil
   157  	}
   158  	m.emit(EventInterfaceUpdate)
   159  	return nil
   160  }
   161  
   162  func getDefaultInterfaceBySocket() (*net.Interface, error) {
   163  	socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
   164  	if err != nil {
   165  		return nil, E.Cause(err, "create file descriptor")
   166  	}
   167  	defer unix.Close(socketFd)
   168  	go unix.Connect(socketFd, &unix.SockaddrInet4{
   169  		Addr: [4]byte{10, 255, 255, 255},
   170  		Port: 80,
   171  	})
   172  	result := make(chan netip.Addr, 1)
   173  	go func() {
   174  		for {
   175  			sockname, sockErr := unix.Getsockname(socketFd)
   176  			if sockErr != nil {
   177  				break
   178  			}
   179  			sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
   180  			if !isInet4Sockaddr {
   181  				break
   182  			}
   183  			addr := netip.AddrFrom4(sockaddr.Addr)
   184  			if addr.IsUnspecified() {
   185  				time.Sleep(time.Millisecond)
   186  				continue
   187  			}
   188  			result <- addr
   189  			break
   190  		}
   191  	}()
   192  	var selectedAddr netip.Addr
   193  	select {
   194  	case selectedAddr = <-result:
   195  	case <-time.After(time.Second):
   196  		return nil, os.ErrDeadlineExceeded
   197  	}
   198  	interfaces, err := net.Interfaces()
   199  	if err != nil {
   200  		return nil, E.Cause(err, "net.Interfaces")
   201  	}
   202  	for _, netInterface := range interfaces {
   203  		interfaceAddrs, err := netInterface.Addrs()
   204  		if err != nil {
   205  			return nil, E.Cause(err, "net.Interfaces.Addrs")
   206  		}
   207  		for _, interfaceAddr := range interfaceAddrs {
   208  			ipNet, isIPNet := interfaceAddr.(*net.IPNet)
   209  			if !isIPNet {
   210  				continue
   211  			}
   212  			if ipNet.Contains(selectedAddr.AsSlice()) {
   213  				return &netInterface, nil
   214  			}
   215  		}
   216  	}
   217  	return nil, E.New("no interface found for address ", selectedAddr)
   218  }