Refactor api

This commit is contained in:
世界 2022-08-14 11:52:38 +08:00
parent d378b6ca53
commit b828f01643
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
15 changed files with 357 additions and 138 deletions

View file

@ -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 .
continue-on-error: true
- name: Build
run: |
make test
make lint_install
make lint

View file

@ -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'

20
Makefile Normal file
View file

@ -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 .

2
go.mod
View file

@ -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

4
go.sum
View file

@ -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=

21
lwip.go
View file

@ -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)

81
tun.go
View file

@ -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
}

View file

@ -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
}

View file

@ -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{}
}

View file

@ -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
}

View file

@ -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,
})
}

View file

@ -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
}

70
tun_test.go Normal file
View file

@ -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)
}
}
}

View file

@ -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 {

View file

@ -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 {