From 2a1554dfd32ec23fbcab4d72a222d10af815dcdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 4 Aug 2022 18:23:58 +0800 Subject: [PATCH] Add darwin monitor --- go.mod | 5 +- go.sum | 10 +-- gvisor.go | 2 + gvisor_err.go | 2 + gvisor_log.go | 2 + gvisor_udp.go | 2 + monitor_android.go | 5 +- monitor_darwin.go | 171 +++++++++++++++++++++++++++++++++++++++++++++ monitor_other.go | 2 +- tun.go | 2 + tun_linux.go | 2 + tun_other.go | 2 +- tun_stub.go | 7 ++ tun_windows.go | 2 + 14 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 monitor_darwin.go create mode 100644 tun_stub.go diff --git a/go.mod b/go.mod index 3b9658b..27d1779 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.18 require ( github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 - github.com/sagernet/sing v0.0.0-20220730061053-a21e329a2698 - golang.org/x/sys v0.0.0-20220731174439-a90be440212d + github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc + golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b + golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d ) diff --git a/go.sum b/go.sum index 3452b9c..ece4b5d 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,15 @@ github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 h1:hE+vtsjBCCPmxkRz9jZA+CicHgVkDT6H+Av5ZzskVxs= github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/sing v0.0.0-20220730061053-a21e329a2698 h1:wjoF4/FOwze8cN2/EvQyyuq1tzXjxNViPIoqQ7CNIb8= -github.com/sagernet/sing v0.0.0-20220730061053-a21e329a2698/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM= +github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc h1:x7H64IiqyrpxPWl/KrWkknzEK4GmpqgfZeVKFVw6E/M= +github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0= +golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 h1:Y7NOhdqIOU8kYI7BxsgL38d0ot0raxvcW+EMQU2QrT4= +golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= gvisor.dev/gvisor v0.0.0-20220711011657-cecae2f4234d h1:KjI6i6P1ib9DiNdNIN8pb2TXfBewpKHf3O58cjj9vw4= diff --git a/gvisor.go b/gvisor.go index 46b1de3..7401262 100644 --- a/gvisor.go +++ b/gvisor.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import ( diff --git a/gvisor_err.go b/gvisor_err.go index 9030afa..ad8c568 100644 --- a/gvisor_err.go +++ b/gvisor_err.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import ( diff --git a/gvisor_log.go b/gvisor_log.go index 1f27c91..1db5ac4 100644 --- a/gvisor_log.go +++ b/gvisor_log.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import ( diff --git a/gvisor_udp.go b/gvisor_udp.go index 755735e..2c831a1 100644 --- a/gvisor_udp.go +++ b/gvisor_udp.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import ( diff --git a/monitor_android.go b/monitor_android.go index e03f22b..57f88c8 100644 --- a/monitor_android.go +++ b/monitor_android.go @@ -2,7 +2,6 @@ package tun import ( "github.com/sagernet/netlink" - E "github.com/sagernet/sing/common/exceptions" ) func (m *defaultInterfaceMonitor) checkUpdate() error { @@ -19,7 +18,7 @@ func (m *defaultInterfaceMonitor) checkUpdate() error { } if defaultTableIndex == 0 { - return E.New("no route to internet") + return ErrNoRoute } routes, err := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{Table: defaultTableIndex}, netlink.RT_FILTER_TABLE) @@ -47,5 +46,5 @@ func (m *defaultInterfaceMonitor) checkUpdate() error { return nil } - return E.New("no route in the system table") + return ErrNoRoute } diff --git a/monitor_darwin.go b/monitor_darwin.go new file mode 100644 index 0000000..1f1fc6d --- /dev/null +++ b/monitor_darwin.go @@ -0,0 +1,171 @@ +package tun + +import ( + "context" + "net" + "net/netip" + "os" + "sync" + + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/x/list" + + "golang.org/x/net/route" + "golang.org/x/sys/unix" +) + +type networkUpdateMonitor struct { + errorHandler E.Handler + + access sync.Mutex + callbacks list.List[NetworkUpdateCallback] + routeSocket *os.File +} + +func NewNetworkUpdateMonitor(errorHandler E.Handler) (NetworkUpdateMonitor, error) { + return &networkUpdateMonitor{ + errorHandler: errorHandler, + }, nil +} + +func (m *networkUpdateMonitor) RegisterCallback(callback NetworkUpdateCallback) *list.Element[NetworkUpdateCallback] { + m.access.Lock() + defer m.access.Unlock() + return m.callbacks.PushBack(callback) +} + +func (m *networkUpdateMonitor) UnregisterCallback(element *list.Element[NetworkUpdateCallback]) { + m.access.Lock() + defer m.access.Unlock() + m.callbacks.Remove(element) +} + +func (m *networkUpdateMonitor) emit() { + m.access.Lock() + callbacks := m.callbacks.Array() + m.access.Unlock() + for _, callback := range callbacks { + err := callback() + if err != nil { + m.errorHandler.NewError(context.Background(), err) + } + } +} + +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() + if err != nil { + m.errorHandler.NewError(context.Background(), err) + 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() + } + if !E.IsClosed(err) { + m.errorHandler.NewError(context.Background(), err) + } +} + +func (m *networkUpdateMonitor) Close() error { + return common.Close(common.PtrOrNil(m.routeSocket)) +} + +type defaultInterfaceMonitor struct { + defaultInterfaceName string + defaultInterfaceIndex int + networkMonitor NetworkUpdateMonitor + element *list.Element[NetworkUpdateCallback] + callback DefaultInterfaceUpdateCallback +} + +func NewDefaultInterfaceMonitor(networkMonitor NetworkUpdateMonitor, callback DefaultInterfaceUpdateCallback) (DefaultInterfaceMonitor, error) { + return &defaultInterfaceMonitor{ + networkMonitor: networkMonitor, + callback: callback, + }, nil +} + +func (m *defaultInterfaceMonitor) Start() error { + err := m.checkUpdate() + if err != nil { + return err + } + m.element = m.networkMonitor.RegisterCallback(m.checkUpdate) + return nil +} + +func (m *defaultInterfaceMonitor) Close() error { + m.networkMonitor.UnregisterCallback(m.element) + return nil +} + +func (m *defaultInterfaceMonitor) DefaultInterfaceName() string { + return m.defaultInterfaceName +} + +func (m *defaultInterfaceMonitor) DefaultInterfaceIndex() int { + return m.defaultInterfaceIndex +} + +func (m *defaultInterfaceMonitor) checkUpdate() error { + ribMessage, err := route.FetchRIB(unix.AF_UNSPEC, route.RIBTypeRoute, 0) + if err != nil { + return err + } + routeMessages, err := route.ParseRIB(route.RIBTypeRoute, ribMessage) + if err != nil { + return err + } + for _, rawRouteMessage := range routeMessages { + routeMessage := rawRouteMessage.(*route.RouteMessage) + if common.Any(common.FilterIsInstance(routeMessage.Addrs, func(it route.Addr) (*route.Inet4Addr, bool) { + addr, loaded := it.(*route.Inet4Addr) + return addr, loaded + }), func(addr *route.Inet4Addr) bool { + return addr.IP == netip.IPv4Unspecified().As4() + }) { + oldInterface := m.defaultInterfaceName + oldIndex := m.defaultInterfaceIndex + + m.defaultInterfaceIndex = routeMessage.Index + defaultInterface, err := net.InterfaceByIndex(routeMessage.Index) + if err != nil { + return err + } + m.defaultInterfaceName = defaultInterface.Name + if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex { + return nil + } + m.callback() + return nil + } + } + return ErrNoRoute +} diff --git a/monitor_other.go b/monitor_other.go index 49b7151..e1fbd71 100644 --- a/monitor_other.go +++ b/monitor_other.go @@ -1,4 +1,4 @@ -//go:build !linux && !windows +//go:build !(linux || windows || darwin) package tun diff --git a/tun.go b/tun.go index a24240f..5745d55 100644 --- a/tun.go +++ b/tun.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import ( diff --git a/tun_linux.go b/tun_linux.go index b91a269..6e3ee06 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import ( diff --git a/tun_other.go b/tun_other.go index d0dee67..2aaeda5 100644 --- a/tun_other.go +++ b/tun_other.go @@ -1,4 +1,4 @@ -//go:build !linux && !windows +//go:build no_gvisor || !(linux || windows) package tun diff --git a/tun_stub.go b/tun_stub.go new file mode 100644 index 0000000..40d0a0b --- /dev/null +++ b/tun_stub.go @@ -0,0 +1,7 @@ +//go:build no_gvisor + +package tun + +type Tun interface { + Close() error +} diff --git a/tun_windows.go b/tun_windows.go index 4aa27ef..a719361 100644 --- a/tun_windows.go +++ b/tun_windows.go @@ -1,3 +1,5 @@ +//go:build !no_gvisor + package tun import (