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

     1  package tun
     2  
     3  import (
     4  	"os"
     5  	"runtime"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/sagernet/netlink"
    10  	E "github.com/sagernet/sing/common/exceptions"
    11  	"github.com/sagernet/sing/common/logger"
    12  	"github.com/sagernet/sing/common/x/list"
    13  
    14  	"golang.org/x/sys/unix"
    15  )
    16  
    17  type networkUpdateMonitor struct {
    18  	routeUpdate chan netlink.RouteUpdate
    19  	linkUpdate  chan netlink.LinkUpdate
    20  	close       chan struct{}
    21  
    22  	access    sync.Mutex
    23  	callbacks list.List[NetworkUpdateCallback]
    24  	logger    logger.Logger
    25  }
    26  
    27  var ErrNetlinkBanned = E.New(
    28  	"netlink socket in Android is banned by Google, " +
    29  		"use the root or system (ADB) user to run sing-box, " +
    30  		"or switch to the sing-box Adnroid graphical interface client",
    31  )
    32  
    33  func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) {
    34  	monitor := &networkUpdateMonitor{
    35  		routeUpdate: make(chan netlink.RouteUpdate, 2),
    36  		linkUpdate:  make(chan netlink.LinkUpdate, 2),
    37  		close:       make(chan struct{}),
    38  		logger:      logger,
    39  	}
    40  	// check is netlink banned by google
    41  	if runtime.GOOS == "android" {
    42  		netlinkSocket, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM, unix.NETLINK_ROUTE)
    43  		if err != nil {
    44  			return nil, ErrNetlinkBanned
    45  		}
    46  		err = unix.Bind(netlinkSocket, &unix.SockaddrNetlink{
    47  			Family: unix.AF_NETLINK,
    48  		})
    49  		unix.Close(netlinkSocket)
    50  		if err != nil {
    51  			return nil, ErrNetlinkBanned
    52  		}
    53  	}
    54  	return monitor, nil
    55  }
    56  
    57  func (m *networkUpdateMonitor) Start() error {
    58  	err := netlink.RouteSubscribe(m.routeUpdate, m.close)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	err = netlink.LinkSubscribe(m.linkUpdate, m.close)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	go m.loopUpdate()
    67  	return nil
    68  }
    69  
    70  func (m *networkUpdateMonitor) loopUpdate() {
    71  	const minDuration = time.Second
    72  	timer := time.NewTimer(minDuration)
    73  	defer timer.Stop()
    74  	for {
    75  		select {
    76  		case <-m.close:
    77  			return
    78  		case <-m.routeUpdate:
    79  		case <-m.linkUpdate:
    80  		}
    81  		m.emit()
    82  		select {
    83  		case <-m.close:
    84  			return
    85  		case <-timer.C:
    86  			timer.Reset(minDuration)
    87  		}
    88  	}
    89  }
    90  
    91  func (m *networkUpdateMonitor) Close() error {
    92  	select {
    93  	case <-m.close:
    94  		return os.ErrClosed
    95  	default:
    96  	}
    97  	close(m.close)
    98  	return nil
    99  }