github.com/metacubex/sing-tun@v0.2.7-0.20240512075008-89e7c6208eec/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  		default:
    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  	done := make(chan struct{})
    71  	go func() {
    72  		select {
    73  		case <-m.done:
    74  			routeSocketFile.Close()
    75  		case <-done:
    76  		}
    77  	}()
    78  	n, err := routeSocketFile.Read(buffer.FreeBytes())
    79  	close(done)
    80  	if err != nil {
    81  		return
    82  	}
    83  	buffer.Truncate(n)
    84  	messages, err := route.ParseRIB(route.RIBTypeRoute, buffer.Bytes())
    85  	if err != nil {
    86  		return
    87  	}
    88  	for _, message := range messages {
    89  		if _, isRouteMessage := message.(*route.RouteMessage); isRouteMessage {
    90  			m.emit()
    91  			return
    92  		}
    93  	}
    94  }
    95  
    96  func (m *networkUpdateMonitor) Close() error {
    97  	m.closeOnce.Do(func() {
    98  		close(m.done)
    99  	})
   100  	return nil
   101  }
   102  
   103  func (m *defaultInterfaceMonitor) checkUpdate() error {
   104  	var (
   105  		defaultInterface *net.Interface
   106  		err              error
   107  	)
   108  	if m.options.UnderNetworkExtension {
   109  		defaultInterface, err = getDefaultInterfaceBySocket()
   110  		if err != nil {
   111  			return err
   112  		}
   113  	} else {
   114  		ribMessage, err := route.FetchRIB(unix.AF_UNSPEC, route.RIBTypeRoute, 0)
   115  		if err != nil {
   116  			return err
   117  		}
   118  		routeMessages, err := route.ParseRIB(route.RIBTypeRoute, ribMessage)
   119  		if err != nil {
   120  			return err
   121  		}
   122  		for _, rawRouteMessage := range routeMessages {
   123  			routeMessage := rawRouteMessage.(*route.RouteMessage)
   124  			if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
   125  				continue
   126  			}
   127  			destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
   128  			if !isIPv4Destination {
   129  				continue
   130  			}
   131  			if destination.IP != netip.IPv4Unspecified().As4() {
   132  				continue
   133  			}
   134  			mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
   135  			if !isIPv4Mask {
   136  				continue
   137  			}
   138  			ones, _ := net.IPMask(mask.IP[:]).Size()
   139  			if ones != 0 {
   140  				continue
   141  			}
   142  			routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
   143  			if err != nil {
   144  				return err
   145  			}
   146  			if routeMessage.Flags&unix.RTF_UP == 0 {
   147  				continue
   148  			}
   149  			if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
   150  				continue
   151  			}
   152  			if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
   153  				// continue
   154  			}
   155  			if routeInterface.Flags&net.FlagLoopback != 0 {
   156  				continue
   157  			}
   158  			defaultInterface = routeInterface
   159  			break
   160  		}
   161  	}
   162  	if defaultInterface == nil {
   163  		return ErrNoRoute
   164  	}
   165  	oldInterface := m.defaultInterfaceName
   166  	oldIndex := m.defaultInterfaceIndex
   167  	m.defaultInterfaceIndex = defaultInterface.Index
   168  	m.defaultInterfaceName = defaultInterface.Name
   169  	if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
   170  		return nil
   171  	}
   172  	m.emit(EventInterfaceUpdate)
   173  	return nil
   174  }
   175  
   176  func getDefaultInterfaceBySocket() (*net.Interface, error) {
   177  	socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
   178  	if err != nil {
   179  		return nil, E.Cause(err, "create file descriptor")
   180  	}
   181  	defer unix.Close(socketFd)
   182  	go unix.Connect(socketFd, &unix.SockaddrInet4{
   183  		Addr: [4]byte{10, 255, 255, 255},
   184  		Port: 80,
   185  	})
   186  	result := make(chan netip.Addr, 1)
   187  	done := make(chan struct{})
   188  	defer close(done)
   189  	go func() {
   190  		for {
   191  			sockname, sockErr := unix.Getsockname(socketFd)
   192  			if sockErr != nil {
   193  				break
   194  			}
   195  			sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
   196  			if !isInet4Sockaddr {
   197  				break
   198  			}
   199  			addr := netip.AddrFrom4(sockaddr.Addr)
   200  			if addr.IsUnspecified() {
   201  				select {
   202  				case <-done:
   203  					break
   204  				default:
   205  					time.Sleep(10 * time.Millisecond)
   206  					continue
   207  				}
   208  			}
   209  			result <- addr
   210  			break
   211  		}
   212  	}()
   213  	var selectedAddr netip.Addr
   214  	select {
   215  	case selectedAddr = <-result:
   216  	case <-time.After(time.Second):
   217  		return nil, nil
   218  	}
   219  	interfaces, err := net.Interfaces()
   220  	if err != nil {
   221  		return nil, E.Cause(err, "net.Interfaces")
   222  	}
   223  	for _, netInterface := range interfaces {
   224  		interfaceAddrs, err := netInterface.Addrs()
   225  		if err != nil {
   226  			return nil, E.Cause(err, "net.Interfaces.Addrs")
   227  		}
   228  		for _, interfaceAddr := range interfaceAddrs {
   229  			ipNet, isIPNet := interfaceAddr.(*net.IPNet)
   230  			if !isIPNet {
   231  				continue
   232  			}
   233  			if ipNet.Contains(selectedAddr.AsSlice()) {
   234  				return &netInterface, nil
   235  			}
   236  		}
   237  	}
   238  	return nil, E.New("no interface found for address ", selectedAddr)
   239  }