diff --git a/monitor.go b/monitor.go index 327eff5..b424256 100644 --- a/monitor.go +++ b/monitor.go @@ -10,14 +10,14 @@ import ( var ErrNoRoute = E.New("no route to internet") type ( - NetworkUpdateCallback = func() error - DefaultInterfaceUpdateCallback = func(event int) error + NetworkUpdateCallback = func() + DefaultInterfaceUpdateCallback = func(event int) ) const ( - EventInterfaceUpdate = iota - EventAndroidVPNUpdate - EventNoRoute + EventInterfaceUpdate = 1 + EventAndroidVPNUpdate = 2 + EventNoRoute = 4 ) type NetworkUpdateMonitor interface { @@ -25,7 +25,6 @@ type NetworkUpdateMonitor interface { Close() error RegisterCallback(callback NetworkUpdateCallback) *list.Element[NetworkUpdateCallback] UnregisterCallback(element *list.Element[NetworkUpdateCallback]) - E.Handler } type DefaultInterfaceMonitor interface { diff --git a/monitor_darwin.go b/monitor_darwin.go index 1df1f21..9455ac3 100644 --- a/monitor_darwin.go +++ b/monitor_darwin.go @@ -1,16 +1,15 @@ package tun import ( - "context" "net" "net/netip" "os" "sync" - "syscall" "time" - "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/x/list" "golang.org/x/net/route" @@ -18,61 +17,63 @@ import ( ) type networkUpdateMonitor struct { - errorHandler E.Handler - access sync.Mutex callbacks list.List[NetworkUpdateCallback] - routeSocket *os.File + routeSocket int + logger logger.Logger } -func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) { - return &networkUpdateMonitor{ - errorHandler: errorHandler, - }, nil +func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) { + return &networkUpdateMonitor{logger: logger}, nil } func (m *networkUpdateMonitor) Start() error { - routeSocket, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0) - if err != nil { - return err - } - err = unix.SetNonblock(routeSocket, true) - if err != nil { - return err - } - m.routeSocket = os.NewFile(uintptr(routeSocket), "route") go m.loopUpdate() return nil } func (m *networkUpdateMonitor) loopUpdate() { - rawConn, err := m.routeSocket.SyscallConn() + for { + err := m.loopUpdate0() + if err != nil { + m.logger.Error("listen network update: ", err) + return + } + } +} + +func (m *networkUpdateMonitor) loopUpdate0() error { + routeSocket, err := unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, 0) + if err != nil { + return err + } + m.loopUpdate1(os.NewFile(uintptr(routeSocket), "route")) + return nil +} + +func (m *networkUpdateMonitor) loopUpdate1(routeSocketFile *os.File) { + defer routeSocketFile.Close() + buffer := buf.NewPacket() + defer buffer.Release() + n, err := routeSocketFile.Read(buffer.FreeBytes()) if err != nil { - m.errorHandler.NewError(context.Background(), E.Cause(err, "create raw route connection")) return } - for { - var innerErr error - err = rawConn.Read(func(fd uintptr) (done bool) { - var msg [2048]byte - _, innerErr = unix.Read(int(fd), msg[:]) - return innerErr != unix.EWOULDBLOCK - }) - if innerErr != nil { - err = innerErr - } - if err != nil { - break - } - m.emit() + buffer.Truncate(n) + messages, err := route.ParseRIB(route.RIBTypeRoute, buffer.Bytes()) + if err != nil { + return } - if err != syscall.EAGAIN { - m.errorHandler.NewError(context.Background(), E.Cause(err, "read route message")) + for _, message := range messages { + if _, isRouteMessage := message.(*route.RouteMessage); isRouteMessage { + m.emit() + return + } } } func (m *networkUpdateMonitor) Close() error { - return common.Close(common.PtrOrNil(m.routeSocket)) + return unix.Close(m.routeSocket) } func (m *defaultInterfaceMonitor) checkUpdate() error { @@ -116,7 +117,7 @@ func (m *defaultInterfaceMonitor) checkUpdate() error { continue } if routeMessage.Flags&unix.RTF_IFSCOPE != 0 { - continue + // continue } defaultInterface = routeInterface break diff --git a/monitor_linux.go b/monitor_linux.go index 32f4537..50885d9 100644 --- a/monitor_linux.go +++ b/monitor_linux.go @@ -5,26 +5,26 @@ import ( "sync" "github.com/sagernet/netlink" - E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/x/list" ) type networkUpdateMonitor struct { - routeUpdate chan netlink.RouteUpdate - linkUpdate chan netlink.LinkUpdate - close chan struct{} - errorHandler E.Handler + routeUpdate chan netlink.RouteUpdate + linkUpdate chan netlink.LinkUpdate + close chan struct{} access sync.Mutex callbacks list.List[NetworkUpdateCallback] + logger logger.Logger } -func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) { +func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) { return &networkUpdateMonitor{ - routeUpdate: make(chan netlink.RouteUpdate, 2), - linkUpdate: make(chan netlink.LinkUpdate, 2), - close: make(chan struct{}), - errorHandler: errorHandler, + routeUpdate: make(chan netlink.RouteUpdate, 2), + linkUpdate: make(chan netlink.LinkUpdate, 2), + close: make(chan struct{}), + logger: logger, }, nil } diff --git a/monitor_linux_default.go b/monitor_linux_default.go index c2292f4..a8446a5 100644 --- a/monitor_linux_default.go +++ b/monitor_linux_default.go @@ -4,8 +4,6 @@ package tun import ( "github.com/sagernet/netlink" - E "github.com/sagernet/sing/common/exceptions" - "golang.org/x/sys/unix" ) @@ -37,5 +35,5 @@ func (m *defaultInterfaceMonitor) checkUpdate() error { m.emit(EventInterfaceUpdate) return nil } - return E.New("no route to internet") + return ErrNoRoute } diff --git a/monitor_other.go b/monitor_other.go index 388c219..76f4c29 100644 --- a/monitor_other.go +++ b/monitor_other.go @@ -3,15 +3,14 @@ package tun import ( + "github.com/sagernet/sing/common/logger" "os" - - E "github.com/sagernet/sing/common/exceptions" ) -func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) { +func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) { return nil, os.ErrInvalid } -func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) { +func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, logger logger.Logger, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) { return nil, os.ErrInvalid } diff --git a/monitor_shared.go b/monitor_shared.go index f6460d4..35abf6e 100644 --- a/monitor_shared.go +++ b/monitor_shared.go @@ -3,7 +3,6 @@ package tun import ( - "context" "errors" "net" "net/netip" @@ -11,7 +10,7 @@ import ( "time" "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/x/list" ) @@ -33,17 +32,10 @@ func (m *networkUpdateMonitor) emit() { callbacks := m.callbacks.Array() m.access.Unlock() for _, callback := range callbacks { - err := callback() - if err != nil { - m.NewError(context.Background(), err) - } + callback() } } -func (m *networkUpdateMonitor) NewError(ctx context.Context, err error) { - m.errorHandler.NewError(ctx, err) -} - type defaultInterfaceMonitor struct { options DefaultInterfaceMonitorOptions networkAddresses []networkAddress @@ -54,6 +46,7 @@ type defaultInterfaceMonitor struct { element *list.Element[NetworkUpdateCallback] access sync.Mutex callbacks list.List[DefaultInterfaceUpdateCallback] + logger logger.Logger } type networkAddress struct { @@ -62,34 +55,35 @@ type networkAddress struct { addresses []netip.Prefix } -func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) { +func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, logger logger.Logger, options DefaultInterfaceMonitorOptions) (DefaultInterfaceMonitor, error) { return &defaultInterfaceMonitor{ options: options, networkMonitor: networkMonitor, defaultInterfaceIndex: -1, + logger: logger, }, nil } func (m *defaultInterfaceMonitor) Start() error { err := m.checkUpdate() if err != nil { - m.networkMonitor.NewError(context.Background(), err) + m.logger.Error("initialize default interface: ", err) } m.element = m.networkMonitor.RegisterCallback(m.delayCheckUpdate) return nil } -func (m *defaultInterfaceMonitor) delayCheckUpdate() error { +func (m *defaultInterfaceMonitor) delayCheckUpdate() { time.Sleep(time.Second) err := m.updateInterfaces() if err != nil { - m.networkMonitor.NewError(context.Background(), E.Cause(err, "update interfaces")) + m.logger.Error("update interfaces: ", err) } err = m.checkUpdate() if errors.Is(err, ErrNoRoute) { + m.defaultInterfaceIndex = -1 m.emit(EventNoRoute) } - return err } func (m *defaultInterfaceMonitor) updateInterfaces() error { @@ -180,9 +174,6 @@ func (m *defaultInterfaceMonitor) emit(event int) { callbacks := m.callbacks.Array() m.access.Unlock() for _, callback := range callbacks { - err := callback(event) - if err != nil { - m.networkMonitor.NewError(context.Background(), err) - } + callback(event) } } diff --git a/monitor_windows.go b/monitor_windows.go index db36d9e..8bd0be4 100644 --- a/monitor_windows.go +++ b/monitor_windows.go @@ -5,6 +5,7 @@ import ( "github.com/sagernet/sing-tun/internal/winipcfg" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" "github.com/sagernet/sing/common/x/list" "golang.org/x/sys/windows" @@ -17,11 +18,12 @@ type networkUpdateMonitor struct { access sync.Mutex callbacks list.List[NetworkUpdateCallback] + logger logger.Logger } -func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) { +func NewNetworkUpdateMonitor(logger logger.Logger) (NetworkUpdateMonitor, error) { return &networkUpdateMonitor{ - errorHandler: errorHandler, + logger: logger, }, nil } diff --git a/tun.go b/tun.go index 57df563..2784339 100644 --- a/tun.go +++ b/tun.go @@ -10,6 +10,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" + "github.com/sagernet/sing/common/logger" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/ranges" ) @@ -47,6 +48,7 @@ type Options struct { InterfaceMonitor DefaultInterfaceMonitor TableIndex int FileDescriptor int + Logger logger.Logger } func CalculateInterfaceName(name string) (tunName string) { diff --git a/tun_linux.go b/tun_linux.go index 1751737..597881f 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -588,15 +588,16 @@ func (t *NativeTun) resetRules() error { return t.setRules() } -func (t *NativeTun) routeUpdate(event int) error { +func (t *NativeTun) routeUpdate(event int) { if event&EventAndroidVPNUpdate == 0 { - return nil + return } err := t.resetRules() if err != nil { - return E.Cause(err, "reset route") + if t.options.Logger != nil { + t.options.Logger.Error(E.Cause(err, "reset route")) + } } - return nil } func (t *NativeTun) setSearchDomainForSystemdResolved() {