From b828f016433365462de2023cf8ea47e16215b402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 14 Aug 2022 11:52:38 +0800 Subject: [PATCH] Refactor api --- .github/workflows/debug.yml | 10 +++- .golangci.yml | 14 ++--- Makefile | 20 +++++++ go.mod | 2 +- go.sum | 4 +- lwip.go | 21 +++++++ tun.go | 81 +++++++++++++++++++++++++++ tun_darwin.go | 60 ++++++++++---------- tun_darwin_gvisor.go | 4 +- tun_linux.go | 89 ++++++++++++++---------------- tun_linux_gvisor.go | 2 +- tun_other.go | 3 +- tun_test.go | 70 +++++++++++++++++++++++ tun_windows.go | 107 +++++++++++++++++++++++------------- tun_windows_gvisor.go | 8 +-- 15 files changed, 357 insertions(+), 138 deletions(-) create mode 100644 Makefile create mode 100644 tun_test.go diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml index fd2846a..8a4a8e5 100644 --- a/.github/workflows/debug.yml +++ b/.github/workflows/debug.yml @@ -3,6 +3,7 @@ name: Debug build on: push: branches: + - main - dev paths-ignore: - '**.md' @@ -29,7 +30,7 @@ jobs: uses: actions/setup-go@v2 with: go-version: ${{ steps.version.outputs.go_version }} - - name: Build + - name: Add cache to Go proxy run: | version=`git rev-parse HEAD` mkdir build @@ -37,4 +38,9 @@ jobs: go mod init build go get -v github.com/sagernet/sing-tun@$version popd - go build -v . \ No newline at end of file + continue-on-error: true + - name: Build + run: | + make test + make lint_install + make lint \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 5505a92..cac85d1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,14 +3,14 @@ linters: enable: - gofumpt - govet - - gci +# - gci - staticcheck linters-settings: - gci: - sections: - - standard - - prefix(github.com/sagernet/sing) - - default +# gci: +# sections: +# - standard +# - prefix(github.com/sagernet/sing) +# - default staticcheck: - go: '1.18' + go: '1.19' diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..dddccee --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +fmt: + @gofumpt -l -w . + @gofmt -s -w . + @gci write -s "standard,prefix(github.com/sagernet/),default" . + +fmt_install: + go install -v mvdan.cc/gofumpt@latest + go install -v github.com/daixiang0/gci@v0.4.0 + +lint: + GOOS=linux golangci-lint run . + GOOS=windows golangci-lint run . + GOOS=darwin golangci-lint run . + GOOS=freebsd golangci-lint run . + +lint_install: + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +test: + go test -v . \ No newline at end of file diff --git a/go.mod b/go.mod index 911a68d..8dc33a6 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/eycorsican/go-tun2socks v1.16.11 github.com/sagernet/netlink v0.0.0-20220803045538-bdac49abf805 - github.com/sagernet/sing v0.0.0-20220807085721-583e78e0b86a + github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 gvisor.dev/gvisor v0.0.0-20220801010827-addd1f7b3e97 diff --git a/go.sum b/go.sum index e59aeee..6840960 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ 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-20220807085721-583e78e0b86a h1:EeaiaHqcGiGQdgRPHf8FPIKb17VADrncz1P27Jfli2w= -github.com/sagernet/sing v0.0.0-20220807085721-583e78e0b86a/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= +github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf h1:JYnx6N1H2KeFKM7YSH5+qfhvvcy6gpqSpewkp1RLRSg= +github.com/sagernet/sing v0.0.0-20220814164830-4f2b872a8cbf/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= 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= diff --git a/lwip.go b/lwip.go index 2ebd1af..c3a47b3 100644 --- a/lwip.go +++ b/lwip.go @@ -54,6 +54,10 @@ func (l *LWIP) Start() error { } func (l *LWIP) loopIn() { + if winTun, isWintun := l.tun.(WinTun); isWintun { + l.loopInWintun(winTun) + return + } mtu := int(l.tunMtu) if runtime.GOOS == "darwin" { mtu += 4 @@ -84,6 +88,23 @@ func (l *LWIP) loopIn() { } } +func (l *LWIP) loopInWintun(tun WinTun) { + for { + packet, release, err := tun.ReadPacket() + if err != nil { + return + } + _, err = l.stack.Write(packet) + release() + if err != nil { + if err.Error() == "stack closed" { + return + } + l.handler.NewError(context.Background(), err) + } + } +} + func (l *LWIP) Close() error { lwip.RegisterTCPConnHandler(nil) lwip.RegisterUDPConnHandler(nil) diff --git a/tun.go b/tun.go index d32fe96..c394471 100644 --- a/tun.go +++ b/tun.go @@ -2,9 +2,18 @@ package tun import ( "io" + "net" + "net/netip" + "runtime" + "sort" + "strconv" + "strings" + "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + F "github.com/sagernet/sing/common/format" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ranges" ) type Handler interface { @@ -17,3 +26,75 @@ type Tun interface { io.ReadWriter Close() error } + +type WinTun interface { + Tun + ReadPacket() ([]byte, func(), error) +} + +type Options struct { + Name string + Inet4Address netip.Prefix + Inet6Address netip.Prefix + MTU uint32 + AutoRoute bool + IncludeUID []ranges.Range[uint32] + IncludeAndroidUser []int + ExcludeUID []ranges.Range[uint32] +} + +func (o Options) ExcludedRanges() (uidRanges []ranges.Range[uint32]) { + var includeAndroidUser []int + if runtime.GOOS == "android" { + includeAndroidUser = o.IncludeAndroidUser + } + return buildExcludedRanges(o.IncludeUID, o.ExcludeUID, includeAndroidUser) +} + +const ( + androidUserRange = 100000 + userEnd uint32 = 0xFFFFFFFF - 1 +) + +func buildExcludedRanges(includeRanges []ranges.Range[uint32], excludeRanges []ranges.Range[uint32], includeAndroidUser []int) (uidRanges []ranges.Range[uint32]) { + if len(includeRanges) > 0 { + uidRanges = includeRanges + } + if len(includeAndroidUser) > 0 { + includeAndroidUser = common.Uniq(includeAndroidUser) + sort.Ints(includeAndroidUser) + for _, androidUser := range includeAndroidUser { + uidRanges = append(uidRanges, ranges.New[uint32](uint32(androidUser)*androidUserRange, uint32(androidUser+1)*androidUserRange-1)) + } + } + if len(uidRanges) > 0 { + uidRanges = ranges.Exclude(uidRanges, excludeRanges) + uidRanges = ranges.Revert(0, userEnd, uidRanges) + } else { + uidRanges = excludeRanges + } + return ranges.Merge(uidRanges) +} + +func DefaultInterfaceName() (tunName string) { + if runtime.GOOS == "darwin" { + tunName = "utun" + } else { + tunName = "tun" + } + interfaces, err := net.Interfaces() + if err != nil { + return + } + var tunIndex int + for _, netInterface := range interfaces { + if strings.HasPrefix(netInterface.Name, tunName) { + index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) + if parseErr == nil { + tunIndex = int(index) + 1 + } + } + } + tunName = F.ToString(tunName, tunIndex) + return +} diff --git a/tun_darwin.go b/tun_darwin.go index a396bd4..5379d83 100644 --- a/tun_darwin.go +++ b/tun_darwin.go @@ -10,8 +10,9 @@ import ( "unsafe" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/sing/common/rw" + N "github.com/sagernet/sing/common/network" "golang.org/x/net/ipv4" "golang.org/x/net/route" @@ -19,18 +20,18 @@ import ( ) type NativeTun struct { - tunFd uintptr tunFile *os.File + tunWriter N.VectorisedWriter + mtu uint32 inet4Address string inet6Address string - mtu uint32 } -func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) { +func Open(options Options) (Tun, error) { ifIndex := -1 - _, err := fmt.Sscanf(name, "utun%d", &ifIndex) + _, err := fmt.Sscanf(options.Name, "utun%d", &ifIndex) if err != nil { - return nil, E.New("bad tun name: ", name) + return nil, E.New("bad tun name: ", options.Name) } tunFd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) @@ -38,19 +39,18 @@ func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu return nil, err } - err = configure(tunFd, ifIndex, name, inet4Address, inet6Address, mtu, autoRoute) + err = configure(tunFd, ifIndex, options.Name, options) if err != nil { unix.Close(tunFd) return nil, err } - nativeTun := &NativeTun{ - tunFd: uintptr(tunFd), tunFile: os.NewFile(uintptr(tunFd), "utun"), - inet4Address: string(inet4Address.Addr().AsSlice()), - inet6Address: string(inet6Address.Addr().AsSlice()), - mtu: mtu, + mtu: options.MTU, + inet4Address: string(options.Inet4Address.Addr().AsSlice()), + inet6Address: string(options.Inet6Address.Addr().AsSlice()), } + nativeTun.tunWriter, _ = bufio.CreateVectorisedWriter(nativeTun.tunFile) runtime.SetFinalizer(nativeTun.tunFile, nil) return nativeTun, nil } @@ -63,7 +63,7 @@ func (t *NativeTun) Read(p []byte) (n int, err error) { copy(p[:], p[4:]) return n - 4, err*/ - return t.tunFile.Write(p) + return t.tunFile.Read(p) } var ( @@ -78,7 +78,7 @@ func (t *NativeTun) Write(p []byte) (n int, err error) { } else { packetHeader = packetHeader6[:] } - _, err = rw.WriteV(t.tunFd, [][]byte{packetHeader, p}) + _, err = bufio.WriteVectorised(t.tunWriter, [][]byte{packetHeader, p}) if err == nil { n = len(p) } @@ -121,7 +121,7 @@ type addrLifetime6 struct { Pltime uint32 } -func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) error { +func configure(tunFd int, ifIndex int, name string, options Options) error { ctlInfo := &unix.CtlInfo{} copy(ctlInfo.Name[:], utunControlName) err := unix.IoctlCtlInfo(tunFd, ctlInfo) @@ -145,28 +145,28 @@ func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, i err = useSocket(unix.AF_INET, unix.SOCK_DGRAM, 0, func(socketFd int) error { var ifr unix.IfreqMTU copy(ifr.Name[:], name) - ifr.MTU = int32(mtu) + ifr.MTU = int32(options.MTU) return unix.IoctlSetIfreqMTU(socketFd, &ifr) }) if err != nil { return err } - if inet4Address.IsValid() { + if options.Inet4Address.IsValid() { ifReq := ifAliasReq{ Addr: unix.RawSockaddrInet4{ Len: unix.SizeofSockaddrInet4, Family: unix.AF_INET, - Addr: inet4Address.Addr().As4(), + Addr: options.Inet4Address.Addr().As4(), }, Dstaddr: unix.RawSockaddrInet4{ Len: unix.SizeofSockaddrInet4, Family: unix.AF_INET, - Addr: inet4Address.Addr().As4(), + Addr: options.Inet4Address.Addr().As4(), }, Mask: unix.RawSockaddrInet4{ Len: unix.SizeofSockaddrInet4, Family: unix.AF_INET, - Addr: netip.MustParseAddr(net.IP(net.CIDRMask(inet4Address.Bits(), 32)).String()).As4(), + Addr: netip.MustParseAddr(net.IP(net.CIDRMask(options.Inet4Address.Bits(), 32)).String()).As4(), }, } copy(ifReq.Name[:], name) @@ -185,17 +185,17 @@ func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, i return err } } - if inet6Address.IsValid() { + if options.Inet6Address.IsValid() { ifReq6 := ifAliasReq6{ Addr: unix.RawSockaddrInet6{ Len: unix.SizeofSockaddrInet6, Family: unix.AF_INET6, - Addr: inet6Address.Addr().As16(), + Addr: options.Inet6Address.Addr().As16(), }, Mask: unix.RawSockaddrInet6{ Len: unix.SizeofSockaddrInet6, Family: unix.AF_INET6, - Addr: netip.MustParseAddr(net.IP(net.CIDRMask(inet6Address.Bits(), 128)).String()).As16(), + Addr: netip.MustParseAddr(net.IP(net.CIDRMask(options.Inet6Address.Bits(), 128)).String()).As16(), }, Flags: IN6_IFF_NODAD | IN6_IFF_SECURED, Lifetime: addrLifetime6{ @@ -203,11 +203,11 @@ func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, i Pltime: ND6_INFINITE_LIFETIME, }, } - if inet6Address.Bits() == 128 { + if options.Inet6Address.Bits() == 128 { ifReq6.Dstaddr = unix.RawSockaddrInet6{ Len: unix.SizeofSockaddrInet6, Family: unix.AF_INET6, - Addr: inet6Address.Addr().Next().As16(), + Addr: options.Inet6Address.Addr().Next().As16(), } } copy(ifReq6.Name[:], name) @@ -226,8 +226,8 @@ func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, i return err } } - if autoRoute { - if inet4Address.IsValid() { + if options.AutoRoute { + if options.Inet4Address.IsValid() { for _, subnet := range []netip.Prefix{ netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 0, 0, 0}), 8), netip.PrefixFrom(netip.AddrFrom4([4]byte{2, 0, 0, 0}), 7), @@ -238,15 +238,15 @@ func configure(tunFd int, ifIndex int, name string, inet4Address netip.Prefix, i netip.PrefixFrom(netip.AddrFrom4([4]byte{64, 0, 0, 0}), 2), netip.PrefixFrom(netip.AddrFrom4([4]byte{128, 0, 0, 0}), 1), } { - err = addRoute(subnet, inet4Address.Addr()) + err = addRoute(subnet, options.Inet4Address.Addr()) if err != nil { return err } } } - if inet6Address.IsValid() { + if options.Inet6Address.IsValid() { subnet := netip.PrefixFrom(netip.AddrFrom16([16]byte{32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), 3) - err = addRoute(subnet, inet6Address.Addr()) + err = addRoute(subnet, options.Inet6Address.Addr()) if err != nil { return err } diff --git a/tun_darwin_gvisor.go b/tun_darwin_gvisor.go index 26f957c..0d5c4a4 100644 --- a/tun_darwin_gvisor.go +++ b/tun_darwin_gvisor.go @@ -5,7 +5,7 @@ package tun import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" - "github.com/sagernet/sing/common/rw" + "github.com/sagernet/sing/common/bufio" "gvisor.dev/gvisor/pkg/bufferv2" "gvisor.dev/gvisor/pkg/tcpip" @@ -122,7 +122,7 @@ func (e *DarwinEndpoint) WritePackets(packetBufferList stack.PacketBufferList) ( case header.IPv6ProtocolNumber: packetHeader = packetHeader6[:] } - _, err := rw.WriteV(e.tun.tunFd, append([][]byte{packetHeader}, packet.AsSlices()...)) + _, err := bufio.WriteVectorised(e.tun.tunWriter, append([][]byte{packetHeader}, packet.AsSlices()...)) if err != nil { return n, &tcpip.ErrAborted{} } diff --git a/tun_linux.go b/tun_linux.go index 17b692d..3c12561 100644 --- a/tun_linux.go +++ b/tun_linux.go @@ -15,32 +15,24 @@ import ( ) type NativeTun struct { - name string - tunFd int - tunFile *os.File - inet4Address netip.Prefix - inet6Address netip.Prefix - mtu uint32 - autoRoute bool + tunFd int + tunFile *os.File + options Options } -func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) { - tunFd, err := open(name) +func Open(options Options) (Tun, error) { + tunFd, err := open(options.Name) if err != nil { return nil, err } - tunLink, err := netlink.LinkByName(name) + tunLink, err := netlink.LinkByName(options.Name) if err != nil { return nil, E.Errors(err, unix.Close(tunFd)) } nativeTun := &NativeTun{ - name: name, - tunFd: tunFd, - tunFile: os.NewFile(uintptr(tunFd), "tun"), - mtu: mtu, - inet4Address: inet4Address, - inet6Address: inet6Address, - autoRoute: autoRoute, + tunFd: tunFd, + tunFile: os.NewFile(uintptr(tunFd), "tun"), + options: options, } runtime.SetFinalizer(nativeTun.tunFile, nil) err = nativeTun.configure(tunLink) @@ -99,7 +91,7 @@ func open(name string) (int, error) { } func (t *NativeTun) configure(tunLink netlink.Link) error { - err := netlink.LinkSetMTU(tunLink, int(t.mtu)) + err := netlink.LinkSetMTU(tunLink, int(t.options.MTU)) if err == unix.EPERM { // unprivileged return nil @@ -107,16 +99,16 @@ func (t *NativeTun) configure(tunLink netlink.Link) error { return err } - if t.inet4Address.IsValid() { - addr4, _ := netlink.ParseAddr(t.inet4Address.String()) + if t.options.Inet4Address.IsValid() { + addr4, _ := netlink.ParseAddr(t.options.Inet4Address.String()) err = netlink.AddrAdd(tunLink, addr4) if err != nil { return err } } - if t.inet6Address.IsValid() { - addr6, _ := netlink.ParseAddr(t.inet6Address.String()) + if t.options.Inet6Address.IsValid() { + addr6, _ := netlink.ParseAddr(t.options.Inet6Address.String()) err = netlink.AddrAdd(tunLink, addr6) if err != nil { return err @@ -128,7 +120,7 @@ func (t *NativeTun) configure(tunLink netlink.Link) error { return err } - if t.autoRoute { + if t.options.AutoRoute { _ = t.unsetRoute0(tunLink) err = t.setRoute(tunLink) if err != nil { @@ -141,7 +133,7 @@ func (t *NativeTun) configure(tunLink netlink.Link) error { func (t *NativeTun) Close() error { var errors []error - if t.autoRoute { + if t.options.AutoRoute { errors = append(errors, t.unsetRoute()) } return E.Errors(append(errors, t.tunFile.Close())...) @@ -151,7 +143,7 @@ const tunTableIndex = 2022 func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route { var routes []netlink.Route - if t.inet4Address.IsValid() { + if t.options.Inet4Address.IsValid() { routes = append(routes, netlink.Route{ Dst: &net.IPNet{ IP: net.IPv4zero, @@ -161,7 +153,7 @@ func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route { Table: tunTableIndex, }) } - if t.inet6Address.IsValid() { + if t.options.Inet6Address.IsValid() { routes = append(routes, netlink.Route{ Dst: &net.IPNet{ IP: net.IPv6zero, @@ -176,21 +168,24 @@ func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route { func (t *NativeTun) rules() []*netlink.Rule { var rules []*netlink.Rule - + var it *netlink.Rule + excludeRanges := t.options.ExcludedRanges() priority := 9000 + nopPriority := priority + 10*(len(excludeRanges)/10+1) - it := netlink.NewRule() - it.Priority = priority - it.Invert = true - it.UIDRange = netlink.NewRuleUIDRange(0, 0xFFFFFFFF-1) - it.Goto = 9100 - rules = append(rules, it) - priority++ - - if t.inet4Address.IsValid() { + for _, excludeRange := range t.options.ExcludedRanges() { it = netlink.NewRule() it.Priority = priority - it.Dst = t.inet4Address.Masked() + it.UIDRange = netlink.NewRuleUIDRange(uint32(excludeRange.Start), uint32(excludeRange.End)) + it.Goto = nopPriority + rules = append(rules, it) + priority++ + } + + if t.options.Inet4Address.IsValid() { + it = netlink.NewRule() + it.Priority = priority + it.Dst = t.options.Inet4Address.Masked() it.Table = tunTableIndex rules = append(rules, it) priority++ @@ -198,15 +193,15 @@ func (t *NativeTun) rules() []*netlink.Rule { it = netlink.NewRule() it.Priority = priority it.IPProto = unix.IPPROTO_ICMP - it.Goto = 9100 + it.Goto = nopPriority rules = append(rules, it) priority++ } - if t.inet6Address.IsValid() { + if t.options.Inet6Address.IsValid() { it = netlink.NewRule() it.Priority = priority - it.Dst = t.inet6Address.Masked() + it.Dst = t.options.Inet6Address.Masked() it.Table = tunTableIndex rules = append(rules, it) priority++ @@ -214,7 +209,7 @@ func (t *NativeTun) rules() []*netlink.Rule { it = netlink.NewRule() it.Priority = priority it.IPProto = unix.IPPROTO_ICMPV6 - it.Goto = 9100 + it.Goto = nopPriority rules = append(rules, it) priority++ } @@ -244,28 +239,28 @@ func (t *NativeTun) rules() []*netlink.Rule { rules = append(rules, it) priority++ - if t.inet4Address.IsValid() { + if t.options.Inet4Address.IsValid() { it = netlink.NewRule() it.Priority = priority it.IifName = "lo" - it.Src = t.inet4Address.Masked() + it.Src = t.options.Inet4Address.Masked() it.Table = tunTableIndex rules = append(rules, it) priority++ } - if t.inet6Address.IsValid() { + if t.options.Inet6Address.IsValid() { it = netlink.NewRule() it.Priority = priority it.IifName = "lo" - it.Src = t.inet6Address.Masked() + it.Src = t.options.Inet6Address.Masked() it.Table = tunTableIndex rules = append(rules, it) priority++ } it = netlink.NewRule() - it.Priority = 9100 + it.Priority = nopPriority rules = append(rules, it) return rules @@ -288,7 +283,7 @@ func (t *NativeTun) setRoute(tunLink netlink.Link) error { } func (t *NativeTun) unsetRoute() error { - tunLink, err := netlink.LinkByName(t.name) + tunLink, err := netlink.LinkByName(t.options.Name) if err != nil { return err } diff --git a/tun_linux_gvisor.go b/tun_linux_gvisor.go index cfd0be1..4dfe587 100644 --- a/tun_linux_gvisor.go +++ b/tun_linux_gvisor.go @@ -12,6 +12,6 @@ var _ GVisorTun = (*NativeTun)(nil) func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, error) { return fdbased.New(&fdbased.Options{ FDs: []int{t.tunFd}, - MTU: t.mtu, + MTU: t.options.MTU, }) } diff --git a/tun_other.go b/tun_other.go index d3c147d..b38fe6a 100644 --- a/tun_other.go +++ b/tun_other.go @@ -3,10 +3,9 @@ package tun import ( - "net/netip" "os" ) -func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) { +func Open(config Options) (Tun, error) { return nil, os.ErrInvalid } diff --git a/tun_test.go b/tun_test.go new file mode 100644 index 0000000..ee120c6 --- /dev/null +++ b/tun_test.go @@ -0,0 +1,70 @@ +package tun + +import ( + "reflect" + "testing" + + "github.com/sagernet/sing/common/ranges" +) + +func TestUIDRanges(t *testing.T) { + for _, testRange := range []struct { + include []ranges.Range[uint32] + exclude []ranges.Range[uint32] + androidUser []int + expected []ranges.Range[uint32] + }{ + {}, + { + include: []ranges.Range[uint32]{ + ranges.NewSingle[uint32](0), + ranges.NewSingle[uint32](1000), + }, + expected: []ranges.Range[uint32]{ + {Start: 1, End: 999}, + {Start: 1001, End: userEnd}, + }, + }, + { + androidUser: []int{0}, + expected: []ranges.Range[uint32]{ + {Start: androidUserRange, End: userEnd}, + }, + }, + { + androidUser: []int{0}, + expected: []ranges.Range[uint32]{ + {Start: 100000, End: userEnd}, + }, + }, + { + androidUser: []int{10}, + include: []ranges.Range[uint32]{ + ranges.NewSingle[uint32](0), + }, + expected: []ranges.Range[uint32]{ + {Start: 1, End: androidUserRange*10 - 1}, + {Start: androidUserRange * 11, End: userEnd}, + }, + }, + { + include: []ranges.Range[uint32]{ + {Start: 123456, End: 123456}, + }, + exclude: []ranges.Range[uint32]{ + {Start: 1000, End: 1000}, + }, + androidUser: []int{0}, + expected: []ranges.Range[uint32]{ + {Start: 1000, End: 1000}, + {Start: 100000, End: 123455}, + {Start: 123457, End: userEnd}, + }, + }, + } { + result := buildExcludedRanges(testRange.include, testRange.exclude, testRange.androidUser) + if !reflect.DeepEqual(result, testRange.expected) { + t.Fatal("input", testRange.include, testRange.exclude, testRange.androidUser, "\nexpected", testRange.expected, "\ngot", result) + } + } +} diff --git a/tun_windows.go b/tun_windows.go index 3dc80dc..ddb5e62 100644 --- a/tun_windows.go +++ b/tun_windows.go @@ -21,33 +21,34 @@ import ( var TunnelType = "sing-tun" type NativeTun struct { - adapter *wintun.Adapter - inet4Address netip.Prefix - inet6Address netip.Prefix - mtu uint32 - autoRoute bool - session wintun.Session - readWait windows.Handle - rate rateJuggler - running sync.WaitGroup - closeOnce sync.Once - close int32 + adapter *wintun.Adapter + options Options + session wintun.Session + readWait windows.Handle + rate rateJuggler + running sync.WaitGroup + closeOnce sync.Once + close int32 } -func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) { - adapter, err := wintun.CreateAdapter(name, TunnelType, generateGUIDByDeviceName(name)) +func Open(options Options) (WinTun, error) { + adapter, err := wintun.CreateAdapter(options.Name, TunnelType, generateGUIDByDeviceName(options.Name)) if err != nil { return nil, err } nativeTun := &NativeTun{ - adapter: adapter, - inet4Address: inet4Address, - inet6Address: inet6Address, - mtu: mtu, - autoRoute: autoRoute, + adapter: adapter, + options: options, } + session, err := adapter.StartSession(0x800000) + if err != nil { + return nil, err + } + nativeTun.session = session + nativeTun.readWait = session.ReadWaitEvent() err = nativeTun.configure() if err != nil { + session.End() adapter.Close() return nil, err } @@ -56,41 +57,41 @@ func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu func (t *NativeTun) configure() error { luid := winipcfg.LUID(t.adapter.LUID()) - if t.inet4Address.IsValid() { - err := luid.SetIPAddressesForFamily(winipcfg.AddressFamily(windows.AF_INET), []netip.Prefix{t.inet4Address}) + if t.options.Inet4Address.IsValid() { + err := luid.SetIPAddressesForFamily(winipcfg.AddressFamily(windows.AF_INET), []netip.Prefix{t.options.Inet4Address}) if err != nil { return E.Cause(err, "set ipv4 address") } } - if t.inet6Address.IsValid() { - err := luid.SetIPAddressesForFamily(winipcfg.AddressFamily(windows.AF_INET6), []netip.Prefix{t.inet6Address}) + if t.options.Inet6Address.IsValid() { + err := luid.SetIPAddressesForFamily(winipcfg.AddressFamily(windows.AF_INET6), []netip.Prefix{t.options.Inet6Address}) if err != nil { return E.Cause(err, "set ipv6 address") } } - err := luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET), []netip.Addr{t.inet4Address.Addr().Next()}, nil) + err := luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET), []netip.Addr{t.options.Inet4Address.Addr().Next()}, nil) if err != nil { return E.Cause(err, "set ipv4 dns") } - err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET6), []netip.Addr{t.inet6Address.Addr().Next()}, nil) + err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET6), []netip.Addr{t.options.Inet6Address.Addr().Next()}, nil) if err != nil { return E.Cause(err, "set ipv6 dns") } - if t.autoRoute { - if t.inet4Address.IsValid() { + if t.options.AutoRoute { + if t.options.Inet4Address.IsValid() { err = luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0) if err != nil { return E.Cause(err, "set ipv4 route") } } - if t.inet6Address.IsValid() { + if t.options.Inet6Address.IsValid() { err = luid.AddRoute(netip.PrefixFrom(netip.IPv6Unspecified(), 0), netip.IPv6Unspecified(), 0) if err != nil { return E.Cause(err, "set ipv6 route") } } } - if t.inet4Address.IsValid() { + if t.options.Inet4Address.IsValid() { var inetIf *winipcfg.MibIPInterfaceRow inetIf, err = luid.IPInterface(winipcfg.AddressFamily(windows.AF_INET)) if err != nil { @@ -101,8 +102,8 @@ func (t *NativeTun) configure() error { inetIf.DadTransmits = 0 inetIf.ManagedAddressConfigurationSupported = false inetIf.OtherStatefulConfigurationSupported = false - inetIf.NLMTU = t.mtu - if t.autoRoute { + inetIf.NLMTU = t.options.MTU + if t.options.AutoRoute { inetIf.UseAutomaticMetric = false inetIf.Metric = 0 } @@ -111,7 +112,7 @@ func (t *NativeTun) configure() error { return E.Cause(err, "set ipv4 options") } } - if t.inet6Address.IsValid() { + if t.options.Inet6Address.IsValid() { var inet6If *winipcfg.MibIPInterfaceRow inet6If, err = luid.IPInterface(winipcfg.AddressFamily(windows.AF_INET6)) if err != nil { @@ -121,8 +122,8 @@ func (t *NativeTun) configure() error { inet6If.DadTransmits = 0 inet6If.ManagedAddressConfigurationSupported = false inet6If.OtherStatefulConfigurationSupported = false - inet6If.NLMTU = t.mtu - if t.autoRoute { + inet6If.NLMTU = t.options.MTU + if t.options.AutoRoute { inet6If.UseAutomaticMetric = false inet6If.Metric = 0 } @@ -136,10 +137,42 @@ func (t *NativeTun) configure() error { } func (t *NativeTun) Read(p []byte) (n int, err error) { - err = t.ReadFunc(func(b []byte) { - n = copy(p, b) - }) - return + return 0, os.ErrInvalid +} + +func (t *NativeTun) ReadPacket() ([]byte, func(), error) { + t.running.Add(1) + defer t.running.Done() +retry: + if atomic.LoadInt32(&t.close) == 1 { + return nil, nil, os.ErrClosed + } + start := nanotime() + shouldSpin := atomic.LoadUint64(&t.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&t.rate.nextStartTime)) <= rateMeasurementGranularity*2 + for { + if atomic.LoadInt32(&t.close) == 1 { + return nil, nil, os.ErrClosed + } + packet, err := t.session.ReceivePacket() + switch err { + case nil: + packetSize := len(packet) + t.rate.update(uint64(packetSize)) + return packet, func() { t.session.ReleaseReceivePacket(packet) }, nil + case windows.ERROR_NO_MORE_ITEMS: + if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration { + windows.WaitForSingleObject(t.readWait, windows.INFINITE) + goto retry + } + procyield(1) + continue + case windows.ERROR_HANDLE_EOF: + return nil, nil, os.ErrClosed + case windows.ERROR_INVALID_DATA: + return nil, nil, errors.New("send ring corrupt") + } + return nil, nil, fmt.Errorf("read failed: %w", err) + } } func (t *NativeTun) ReadFunc(block func(b []byte)) error { diff --git a/tun_windows_gvisor.go b/tun_windows_gvisor.go index 33227e2..fd27621 100644 --- a/tun_windows_gvisor.go +++ b/tun_windows_gvisor.go @@ -12,12 +12,6 @@ import ( var _ GVisorTun = (*NativeTun)(nil) func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, error) { - session, err := t.adapter.StartSession(0x800000) - if err != nil { - return nil, err - } - t.session = session - t.readWait = session.ReadWaitEvent() return &WintunEndpoint{tun: t}, nil } @@ -29,7 +23,7 @@ type WintunEndpoint struct { } func (e *WintunEndpoint) MTU() uint32 { - return e.tun.mtu + return e.tun.options.MTU } func (e *WintunEndpoint) MaxHeaderLength() uint16 {