瀏覽代碼

Add tun inbound for windows

世界 3 年之前
父節點
當前提交
e7d557fd9e

+ 1 - 1
common/dialer/auto_other.go

@@ -1,4 +1,4 @@
-//go:build !linux
+//go:build !linux && !windows
 
 
 package dialer
 package dialer
 
 

+ 68 - 0
common/dialer/auto_windows.go

@@ -0,0 +1,68 @@
+package dialer
+
+import (
+	"encoding/binary"
+	"net"
+	"net/netip"
+	"syscall"
+	"unsafe"
+
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing/common/control"
+	E "github.com/sagernet/sing/common/exceptions"
+
+	"golang.org/x/sys/windows"
+)
+
+const (
+	IP_UNICAST_IF   = 31
+	IPV6_UNICAST_IF = 31
+)
+
+func bind4(handle windows.Handle, ifaceIdx int) error {
+	var bytes [4]byte
+	binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx))
+	idx := *(*uint32)(unsafe.Pointer(&bytes[0]))
+	return windows.SetsockoptInt(handle, windows.IPPROTO_IP, IP_UNICAST_IF, int(idx))
+}
+
+func bind6(handle windows.Handle, ifaceIdx int) error {
+	return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, IPV6_UNICAST_IF, int(ifaceIdx))
+}
+
+func BindToInterface(router adapter.Router) control.Func {
+	return func(network, address string, conn syscall.RawConn) error {
+		interfaceName := router.DefaultInterfaceName()
+		if interfaceName == "" {
+			return nil
+		}
+		ipStr, _, err := net.SplitHostPort(address)
+		if err == nil {
+			if ip, err := netip.ParseAddr(ipStr); err == nil && !ip.IsGlobalUnicast() {
+				return err
+			}
+		}
+		var innerErr error
+		err = conn.Control(func(fd uintptr) {
+			handle := windows.Handle(fd)
+			// handle ip empty, e.g. net.Listen("udp", ":0")
+			if ipStr == "" {
+				innerErr = bind4(handle, router.DefaultInterfaceIndex())
+				if innerErr != nil {
+					return
+				}
+				// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
+				bind6(handle, router.DefaultInterfaceIndex())
+				return
+			}
+
+			switch network {
+			case "tcp4", "udp4", "ip4":
+				innerErr = bind4(handle, router.DefaultInterfaceIndex())
+			case "tcp6", "udp6":
+				innerErr = bind6(handle, router.DefaultInterfaceIndex())
+			}
+		})
+		return E.Errors(innerErr, err)
+	}
+}

+ 0 - 9
common/iffmonitor/monitor.go

@@ -1,9 +0,0 @@
-package iffmonitor
-
-import "github.com/sagernet/sing-box/adapter"
-
-type InterfaceMonitor interface {
-	adapter.Service
-	DefaultInterfaceName() string
-	DefaultInterfaceIndex() int
-}

+ 0 - 108
common/iffmonitor/monitor_linux.go

@@ -1,108 +0,0 @@
-package iffmonitor
-
-import (
-	"os"
-
-	"github.com/sagernet/sing-box/log"
-	E "github.com/sagernet/sing/common/exceptions"
-
-	"github.com/vishvananda/netlink"
-)
-
-var _ InterfaceMonitor = (*monitor)(nil)
-
-type monitor struct {
-	logger                log.Logger
-	defaultInterfaceName  string
-	defaultInterfaceIndex int
-	update                chan netlink.RouteUpdate
-	close                 chan struct{}
-}
-
-func New(logger log.Logger) (InterfaceMonitor, error) {
-	return &monitor{
-		logger: logger,
-		update: make(chan netlink.RouteUpdate, 2),
-		close:  make(chan struct{}),
-	}, nil
-}
-
-func (m *monitor) Start() error {
-	err := netlink.RouteSubscribe(m.update, m.close)
-	if err != nil {
-		return err
-	}
-	err = m.checkUpdate()
-	if err != nil {
-		return err
-	}
-	go m.loopUpdate()
-	return nil
-}
-
-func (m *monitor) loopUpdate() {
-	for {
-		select {
-		case <-m.close:
-			return
-		case <-m.update:
-			err := m.checkUpdate()
-			if err != nil {
-				m.logger.Error(E.Cause(err, "check default interface"))
-			}
-		}
-	}
-}
-
-func (m *monitor) checkUpdate() error {
-	routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
-	if err != nil {
-		return err
-	}
-	for _, route := range routes {
-		if route.Dst != nil {
-			continue
-		}
-		var link netlink.Link
-		link, err = netlink.LinkByIndex(route.LinkIndex)
-		if err != nil {
-			return err
-		}
-
-		if link.Type() == "tuntap" {
-			continue
-		}
-
-		oldInterface := m.defaultInterfaceName
-		oldIndex := m.defaultInterfaceIndex
-
-		m.defaultInterfaceName = link.Attrs().Name
-		m.defaultInterfaceIndex = link.Attrs().Index
-
-		if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
-			return nil
-		}
-
-		m.logger.Info("updated default interface ", m.defaultInterfaceName, ", index ", m.defaultInterfaceIndex)
-		return nil
-	}
-	return E.New("no route to internet")
-}
-
-func (m *monitor) Close() error {
-	select {
-	case <-m.close:
-		return os.ErrClosed
-	default:
-	}
-	close(m.close)
-	return nil
-}
-
-func (m *monitor) DefaultInterfaceName() string {
-	return m.defaultInterfaceName
-}
-
-func (m *monitor) DefaultInterfaceIndex() int {
-	return m.defaultInterfaceIndex
-}

+ 0 - 13
common/iffmonitor/monitor_other.go

@@ -1,13 +0,0 @@
-//go:build !linux
-
-package iffmonitor
-
-import (
-	"os"
-
-	"github.com/sagernet/sing-box/log"
-)
-
-func New(logger log.Logger) (InterfaceMonitor, error) {
-	return nil, os.ErrInvalid
-}

+ 1 - 0
docs/configuration/inbound/index.md

@@ -15,6 +15,7 @@
 
 
 | Type          | Format                       |
 | Type          | Format                       |
 |---------------|------------------------------|
 |---------------|------------------------------|
+| `tun`         | [Tun](./tun)                 |
 | `direct`      | [Direct](./direct)           |
 | `direct`      | [Direct](./direct)           |
 | `mixed`       | [Mixed](./mixed)             |
 | `mixed`       | [Mixed](./mixed)             |
 | `socks`       | [Socks](./socks)             |
 | `socks`       | [Socks](./socks)             |

+ 1 - 1
docs/configuration/inbound/tun.md

@@ -1,6 +1,6 @@
 !!! error ""
 !!! error ""
 
 
-    Linux only
+    Linux and Windows only
 
 
 ### Structure
 ### Structure
 
 

+ 1 - 1
docs/configuration/route/index.md

@@ -28,7 +28,7 @@ Default outbound tag. the first outbound will be used if empty.
 
 
 !!! error ""
 !!! error ""
 
 
-    Linux only
+    Linux and Windows only
 
 
 Bind outbound connections to the default NIC by default to prevent routing loops under Tun.
 Bind outbound connections to the default NIC by default to prevent routing loops under Tun.
 
 

+ 3 - 3
go.mod

@@ -10,12 +10,12 @@ require (
 	github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91
 	github.com/sagernet/sing v0.0.0-20220712060558-029ab1ce4f91
 	github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619
 	github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
-	github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96
+	github.com/sagernet/sing-tun v0.0.0-20220713125153-6c2c28da9d76
 	github.com/spf13/cobra v1.5.0
 	github.com/spf13/cobra v1.5.0
 	github.com/stretchr/testify v1.8.0
 	github.com/stretchr/testify v1.8.0
-	github.com/vishvananda/netlink v1.1.0
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
 	golang.org/x/net v0.0.0-20220708220712-1185a9018129
 	golang.org/x/net v0.0.0-20220708220712-1185a9018129
+	golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e
 )
 )
 
 
 require (
 require (
@@ -26,8 +26,8 @@ require (
 	github.com/kr/pretty v0.1.0 // indirect
 	github.com/kr/pretty v0.1.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/vishvananda/netlink v1.1.0 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
-	golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
 	gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect

+ 4 - 4
go.sum

@@ -31,8 +31,8 @@ github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZ
 github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
 github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
-github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 h1:BPsCEEKmww4PCuL2qCKGpwuS/HllNz4/G7EjvSHlXXg=
-github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96/go.mod h1:OLQnVTGk8NMVdoegQvenGHsGEv3diSMWe9Uh02cel0E=
+github.com/sagernet/sing-tun v0.0.0-20220713125153-6c2c28da9d76 h1:/nvko0np1sAZrY5s+0HIn99SXbAkN7lPWiBZ22nDEL0=
+github.com/sagernet/sing-tun v0.0.0-20220713125153-6c2c28da9d76/go.mod h1:oIK1kg8hkeA5zNSv9BcbTPzdR00bbVBt6eYvJp+rsck=
 github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
 github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
 github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
 github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -53,8 +53,8 @@ golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6fl
 golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
-golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew=
+golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/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 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

+ 10 - 20
inbound/tun.go

@@ -1,12 +1,9 @@
-//go:build !no_tun
-
 package inbound
 package inbound
 
 
 import (
 import (
 	"context"
 	"context"
 	"net"
 	"net"
 	"net/netip"
 	"net/netip"
-	"os"
 	"runtime"
 	"runtime"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
@@ -17,6 +14,7 @@ import (
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing-tun"
+	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	F "github.com/sagernet/sing/common/format"
 	F "github.com/sagernet/sing/common/format"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
@@ -40,8 +38,8 @@ type Tun struct {
 	autoRoute      bool
 	autoRoute      bool
 	hijackDNS      bool
 	hijackDNS      bool
 
 
-	tunFd uintptr
-	tun   *tun.GVisorTun
+	tunIf    tun.Tun
+	tunStack *tun.GVisorTun
 }
 }
 
 
 func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
 func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
@@ -77,17 +75,13 @@ func (t *Tun) Tag() string {
 }
 }
 
 
 func (t *Tun) Start() error {
 func (t *Tun) Start() error {
-	tunFd, err := tun.Open(t.tunName)
-	if err != nil {
-		return E.Cause(err, "create tun interface")
-	}
-	err = tun.Configure(t.tunName, t.inet4Address, t.inet6Address, t.tunMTU, t.autoRoute)
+	tunIf, err := tun.Open(t.tunName, t.inet4Address, t.inet6Address, t.tunMTU, t.autoRoute)
 	if err != nil {
 	if err != nil {
 		return E.Cause(err, "configure tun interface")
 		return E.Cause(err, "configure tun interface")
 	}
 	}
-	t.tunFd = tunFd
-	t.tun = tun.NewGVisor(t.ctx, tunFd, t.tunMTU, t)
-	err = t.tun.Start()
+	t.tunIf = tunIf
+	t.tunStack = tun.NewGVisor(t.ctx, tunIf, t.tunMTU, t)
+	err = t.tunStack.Start()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -96,13 +90,9 @@ func (t *Tun) Start() error {
 }
 }
 
 
 func (t *Tun) Close() error {
 func (t *Tun) Close() error {
-	err := tun.UnConfigure(t.tunName, t.inet4Address, t.inet6Address, t.autoRoute)
-	if err != nil {
-		return err
-	}
-	return E.Errors(
-		t.tun.Close(),
-		os.NewFile(t.tunFd, "tun").Close(),
+	return common.Close(
+		t.tunStack,
+		t.tunIf,
 	)
 	)
 }
 }
 
 

+ 0 - 16
inbound/tun_disabled.go

@@ -1,16 +0,0 @@
-//go:build no_tun
-
-package inbound
-
-import (
-	"context"
-
-	"github.com/sagernet/sing-box/adapter"
-	"github.com/sagernet/sing-box/log"
-	"github.com/sagernet/sing-box/option"
-	E "github.com/sagernet/sing/common/exceptions"
-)
-
-func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (adapter.Inbound, error) {
-	return nil, E.New("tun disabled in this build")
-}

+ 7 - 7
log/default.go

@@ -63,31 +63,31 @@ func (l *simpleLogger) Log(ctx context.Context, level Level, args []any) {
 }
 }
 
 
 func (l *simpleLogger) Trace(args ...any) {
 func (l *simpleLogger) Trace(args ...any) {
-	l.Log(nil, LevelTrace, args)
+	l.TraceContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) Debug(args ...any) {
 func (l *simpleLogger) Debug(args ...any) {
-	l.Log(nil, LevelDebug, args)
+	l.DebugContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) Info(args ...any) {
 func (l *simpleLogger) Info(args ...any) {
-	l.Log(nil, LevelInfo, args)
+	l.InfoContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) Warn(args ...any) {
 func (l *simpleLogger) Warn(args ...any) {
-	l.Log(nil, LevelWarn, args)
+	l.WarnContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) Error(args ...any) {
 func (l *simpleLogger) Error(args ...any) {
-	l.Log(nil, LevelError, args)
+	l.ErrorContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) Fatal(args ...any) {
 func (l *simpleLogger) Fatal(args ...any) {
-	l.Log(nil, LevelFatal, args)
+	l.FatalContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) Panic(args ...any) {
 func (l *simpleLogger) Panic(args ...any) {
-	l.Log(nil, LevelPanic, args)
+	l.PanicContext(context.Background(), args...)
 }
 }
 
 
 func (l *simpleLogger) TraceContext(ctx context.Context, args ...any) {
 func (l *simpleLogger) TraceContext(ctx context.Context, args ...any) {

+ 7 - 7
log/observable.go

@@ -80,31 +80,31 @@ func (l *observableLogger) Log(ctx context.Context, level Level, args []any) {
 }
 }
 
 
 func (l *observableLogger) Trace(args ...any) {
 func (l *observableLogger) Trace(args ...any) {
-	l.Log(nil, LevelTrace, args)
+	l.TraceContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) Debug(args ...any) {
 func (l *observableLogger) Debug(args ...any) {
-	l.Log(nil, LevelDebug, args)
+	l.DebugContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) Info(args ...any) {
 func (l *observableLogger) Info(args ...any) {
-	l.Log(nil, LevelInfo, args)
+	l.InfoContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) Warn(args ...any) {
 func (l *observableLogger) Warn(args ...any) {
-	l.Log(nil, LevelWarn, args)
+	l.WarnContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) Error(args ...any) {
 func (l *observableLogger) Error(args ...any) {
-	l.Log(nil, LevelError, args)
+	l.ErrorContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) Fatal(args ...any) {
 func (l *observableLogger) Fatal(args ...any) {
-	l.Log(nil, LevelFatal, args)
+	l.FatalContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) Panic(args ...any) {
 func (l *observableLogger) Panic(args ...any) {
-	l.Log(nil, LevelPanic, args)
+	l.PanicContext(context.Background(), args...)
 }
 }
 
 
 func (l *observableLogger) TraceContext(ctx context.Context, args ...any) {
 func (l *observableLogger) TraceContext(ctx context.Context, args ...any) {

+ 7 - 5
route/router.go

@@ -17,12 +17,12 @@ import (
 	"github.com/sagernet/sing-box/common/dialer"
 	"github.com/sagernet/sing-box/common/dialer"
 	"github.com/sagernet/sing-box/common/geoip"
 	"github.com/sagernet/sing-box/common/geoip"
 	"github.com/sagernet/sing-box/common/geosite"
 	"github.com/sagernet/sing-box/common/geosite"
-	"github.com/sagernet/sing-box/common/iffmonitor"
 	"github.com/sagernet/sing-box/common/sniff"
 	"github.com/sagernet/sing-box/common/sniff"
 	C "github.com/sagernet/sing-box/constant"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-dns"
+	tun "github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing/common"
 	"github.com/sagernet/sing/common"
 	"github.com/sagernet/sing/common/buf"
 	"github.com/sagernet/sing/common/buf"
 	"github.com/sagernet/sing/common/bufio"
 	"github.com/sagernet/sing/common/bufio"
@@ -65,7 +65,7 @@ type Router struct {
 	transportMap          map[string]dns.Transport
 	transportMap          map[string]dns.Transport
 
 
 	autoDetectInterface bool
 	autoDetectInterface bool
-	interfaceMonitor    iffmonitor.InterfaceMonitor
+	interfaceMonitor    tun.InterfaceMonitor
 }
 }
 
 
 func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) {
 func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) {
@@ -176,7 +176,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
 		return nil, E.New("found circular reference in dns servers: ", strings.Join(unresolvedTags, " "))
 		return nil, E.New("found circular reference in dns servers: ", strings.Join(unresolvedTags, " "))
 	}
 	}
 	var defaultTransport dns.Transport
 	var defaultTransport dns.Transport
-	if options.Final != "" {
+	if dnsOptions.Final != "" {
 		defaultTransport = dummyTransportMap[options.Final]
 		defaultTransport = dummyTransportMap[options.Final]
 		if defaultTransport == nil {
 		if defaultTransport == nil {
 			return nil, E.New("default dns server not found: ", options.Final)
 			return nil, E.New("default dns server not found: ", options.Final)
@@ -193,9 +193,11 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
 	router.transportMap = transportMap
 	router.transportMap = transportMap
 
 
 	if options.AutoDetectInterface {
 	if options.AutoDetectInterface {
-		monitor, err := iffmonitor.New(router.logger)
+		monitor, err := tun.NewMonitor(func() {
+			router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(), ", index ", router.interfaceMonitor.DefaultInterfaceIndex())
+		})
 		if err != nil {
 		if err != nil {
-			return nil, E.Cause(err, "create default interface monitor")
+			return nil, E.New("auto_detect_interface unsupported on current platform")
 		}
 		}
 		router.interfaceMonitor = monitor
 		router.interfaceMonitor = monitor
 	}
 	}

+ 2 - 2
test/go.mod

@@ -35,11 +35,11 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 // indirect
 	github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 // indirect
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 // indirect
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 // indirect
-	github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 // indirect
+	github.com/sagernet/sing-tun v0.0.0-20220713125153-6c2c28da9d76 // indirect
 	github.com/vishvananda/netlink v1.1.0 // indirect
 	github.com/vishvananda/netlink v1.1.0 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
-	golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
+	golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect
 	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.3.0 // indirect
 	gotest.tools/v3 v3.3.0 // indirect

+ 4 - 4
test/go.sum

@@ -58,8 +58,8 @@ github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 h1:oHbOmq1WS0XaZ
 github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
 github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619/go.mod h1:y2fpvoxukw3G7eApIZwkcpcG/NE4AB8pCQI0Qd8rMqk=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
-github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96 h1:BPsCEEKmww4PCuL2qCKGpwuS/HllNz4/G7EjvSHlXXg=
-github.com/sagernet/sing-tun v0.0.0-20220711091522-4f7247190c96/go.mod h1:OLQnVTGk8NMVdoegQvenGHsGEv3diSMWe9Uh02cel0E=
+github.com/sagernet/sing-tun v0.0.0-20220713125153-6c2c28da9d76 h1:/nvko0np1sAZrY5s+0HIn99SXbAkN7lPWiBZ22nDEL0=
+github.com/sagernet/sing-tun v0.0.0-20220713125153-6c2c28da9d76/go.mod h1:oIK1kg8hkeA5zNSv9BcbTPzdR00bbVBt6eYvJp+rsck=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -103,8 +103,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
-golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew=
+golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
 golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=