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