Browse Source

Add route.default_interface option

世界 3 years ago
parent
commit
377f3f83a2

+ 3 - 2
adapter/router.go

@@ -30,9 +30,10 @@ type Router interface {
 	LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
 	LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
 
 
 	InterfaceBindManager() control.BindManager
 	InterfaceBindManager() control.BindManager
+	DefaultInterface() string
 	AutoDetectInterface() bool
 	AutoDetectInterface() bool
-	DefaultInterfaceName() string
-	DefaultInterfaceIndex() int
+	AutoDetectInterfaceName() string
+	AutoDetectInterfaceIndex() int
 }
 }
 
 
 type Rule interface {
 type Rule interface {

+ 1 - 1
common/dialer/auto_linux.go

@@ -10,7 +10,7 @@ import (
 
 
 func BindToInterface(router adapter.Router) control.Func {
 func BindToInterface(router adapter.Router) control.Func {
 	return func(network, address string, conn syscall.RawConn) error {
 	return func(network, address string, conn syscall.RawConn) error {
-		interfaceName := router.DefaultInterfaceName()
+		interfaceName := router.AutoDetectInterfaceName()
 		if interfaceName == "" {
 		if interfaceName == "" {
 			return nil
 			return nil
 		}
 		}

+ 5 - 5
common/dialer/auto_windows.go

@@ -32,7 +32,7 @@ func bind6(handle windows.Handle, ifaceIdx int) error {
 
 
 func BindToInterface(router adapter.Router) control.Func {
 func BindToInterface(router adapter.Router) control.Func {
 	return func(network, address string, conn syscall.RawConn) error {
 	return func(network, address string, conn syscall.RawConn) error {
-		interfaceName := router.DefaultInterfaceName()
+		interfaceName := router.AutoDetectInterfaceName()
 		if interfaceName == "" {
 		if interfaceName == "" {
 			return nil
 			return nil
 		}
 		}
@@ -47,20 +47,20 @@ func BindToInterface(router adapter.Router) control.Func {
 			handle := windows.Handle(fd)
 			handle := windows.Handle(fd)
 			// handle ip empty, e.g. net.Listen("udp", ":0")
 			// handle ip empty, e.g. net.Listen("udp", ":0")
 			if ipStr == "" {
 			if ipStr == "" {
-				innerErr = bind4(handle, router.DefaultInterfaceIndex())
+				innerErr = bind4(handle, router.AutoDetectInterfaceIndex())
 				if innerErr != nil {
 				if innerErr != nil {
 					return
 					return
 				}
 				}
 				// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
 				// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6
-				bind6(handle, router.DefaultInterfaceIndex())
+				bind6(handle, router.AutoDetectInterfaceIndex())
 				return
 				return
 			}
 			}
 
 
 			switch network {
 			switch network {
 			case "tcp4", "udp4", "ip4":
 			case "tcp4", "udp4", "ip4":
-				innerErr = bind4(handle, router.DefaultInterfaceIndex())
+				innerErr = bind4(handle, router.AutoDetectInterfaceIndex())
 			case "tcp6", "udp6":
 			case "tcp6", "udp6":
-				innerErr = bind6(handle, router.DefaultInterfaceIndex())
+				innerErr = bind6(handle, router.AutoDetectInterfaceIndex())
 			}
 			}
 		})
 		})
 		return E.Errors(innerErr, err)
 		return E.Errors(innerErr, err)

+ 7 - 4
common/dialer/default.go

@@ -29,19 +29,22 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
 	} else if router.AutoDetectInterface() {
 	} else if router.AutoDetectInterface() {
 		if runtime.GOOS == "windows" {
 		if runtime.GOOS == "windows" {
 			dialer.Control = control.Append(dialer.Control, control.BindToInterfaceIndexFunc(func() int {
 			dialer.Control = control.Append(dialer.Control, control.BindToInterfaceIndexFunc(func() int {
-				return router.DefaultInterfaceIndex()
+				return router.AutoDetectInterfaceIndex()
 			}))
 			}))
 			listener.Control = control.Append(listener.Control, control.BindToInterfaceIndexFunc(func() int {
 			listener.Control = control.Append(listener.Control, control.BindToInterfaceIndexFunc(func() int {
-				return router.DefaultInterfaceIndex()
+				return router.AutoDetectInterfaceIndex()
 			}))
 			}))
 		} else {
 		} else {
 			dialer.Control = control.Append(dialer.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
 			dialer.Control = control.Append(dialer.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
-				return router.DefaultInterfaceName()
+				return router.AutoDetectInterfaceName()
 			}))
 			}))
 			listener.Control = control.Append(listener.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
 			listener.Control = control.Append(listener.Control, control.BindToInterfaceFunc(router.InterfaceBindManager(), func() string {
-				return router.DefaultInterfaceName()
+				return router.AutoDetectInterfaceName()
 			}))
 			}))
 		}
 		}
+	} else if router.DefaultInterface() != "" {
+		dialer.Control = control.Append(dialer.Control, control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface()))
+		listener.Control = control.Append(listener.Control, control.BindToInterface(router.InterfaceBindManager(), router.DefaultInterface()))
 	}
 	}
 	if options.RoutingMark != 0 {
 	if options.RoutingMark != 0 {
 		dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
 		dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))

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

@@ -47,7 +47,7 @@ Set the default route to the Tun.
 
 
 !!! error ""
 !!! error ""
 
 
-    To avoid traffic loopback, set `route.auto_delect_interface` or `outbound.bind_interface`
+    To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
 
 
 #### hijack_dns
 #### hijack_dns
 
 

+ 13 - 2
docs/configuration/route/index.md

@@ -7,7 +7,8 @@
     "geosite": {},
     "geosite": {},
     "rules": [],
     "rules": [],
     "final": "",
     "final": "",
-    "auto_detect_interface": false
+    "auto_detect_interface": false,
+    "default_interface": "en0"
   }
   }
 }
 }
 ```
 ```
@@ -32,4 +33,14 @@ Default outbound tag. the first outbound will be used if empty.
 
 
 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.
 
 
-Takes no effect if `outbound.bind_interface` is set.
+Takes no effect if `outbound.bind_interface` is set.
+
+#### default_interface
+
+!!! error ""
+
+    Linux and Windows only
+
+Bind outbound connections to the specified NIC by default to prevent routing loops under Tun.
+
+Takes no effect if `auto_detect_interface` is set.

+ 1 - 1
go.mod

@@ -10,7 +10,7 @@ require (
 	github.com/sagernet/sing v0.0.0-20220714145306-09b55ce4b6d0
 	github.com/sagernet/sing v0.0.0-20220714145306-09b55ce4b6d0
 	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-20220715031600-dacfbcd606f4
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4
-	github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361
+	github.com/sagernet/sing-tun v0.0.0-20220715065926-9dc73c0bcc1d
 	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
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
 	golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d

+ 2 - 2
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-20220715031600-dacfbcd606f4 h1:s3yKbPfiNwFFGfcNUs8NcKYzor78HWmZuDl5B0dsVQI=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4 h1:s3yKbPfiNwFFGfcNUs8NcKYzor78HWmZuDl5B0dsVQI=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
-github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361 h1:M6m9mXG5u151voF0wSDLf5JoDwHU5+4FOMrzb/kaRdc=
-github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo=
+github.com/sagernet/sing-tun v0.0.0-20220715065926-9dc73c0bcc1d h1:wt+OEJA3EiLIjwp+DROTtIXLox+9dwMRsVY/K2MvjXo=
+github.com/sagernet/sing-tun v0.0.0-20220715065926-9dc73c0bcc1d/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo=
 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=

+ 1 - 0
option/route.go

@@ -14,6 +14,7 @@ type RouteOptions struct {
 	Rules               []Rule          `json:"rules,omitempty"`
 	Rules               []Rule          `json:"rules,omitempty"`
 	Final               string          `json:"final,omitempty"`
 	Final               string          `json:"final,omitempty"`
 	AutoDetectInterface bool            `json:"auto_detect_interface,omitempty"`
 	AutoDetectInterface bool            `json:"auto_detect_interface,omitempty"`
+	DefaultInterface    string          `json:"default_interface,omitempty"`
 }
 }
 
 
 func (o RouteOptions) Equals(other RouteOptions) bool {
 func (o RouteOptions) Equals(other RouteOptions) bool {

+ 8 - 2
route/router.go

@@ -68,6 +68,7 @@ type Router struct {
 	interfaceBindManager control.BindManager
 	interfaceBindManager control.BindManager
 	networkMonitor       tun.NetworkUpdateMonitor
 	networkMonitor       tun.NetworkUpdateMonitor
 	autoDetectInterface  bool
 	autoDetectInterface  bool
+	defaultInterface     string
 	interfaceMonitor     tun.DefaultInterfaceMonitor
 	interfaceMonitor     tun.DefaultInterfaceMonitor
 }
 }
 
 
@@ -89,6 +90,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
 		defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
 		defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
 		interfaceBindManager:  control.NewBindManager(),
 		interfaceBindManager:  control.NewBindManager(),
 		autoDetectInterface:   options.AutoDetectInterface,
 		autoDetectInterface:   options.AutoDetectInterface,
+		defaultInterface:      options.DefaultInterface,
 	}
 	}
 	for i, ruleOptions := range options.Rules {
 	for i, ruleOptions := range options.Rules {
 		routeRule, err := NewRule(router, logger, ruleOptions)
 		routeRule, err := NewRule(router, logger, ruleOptions)
@@ -540,14 +542,18 @@ func (r *Router) AutoDetectInterface() bool {
 	return r.autoDetectInterface
 	return r.autoDetectInterface
 }
 }
 
 
-func (r *Router) DefaultInterfaceName() string {
+func (r *Router) DefaultInterface() string {
+	return r.defaultInterface
+}
+
+func (r *Router) AutoDetectInterfaceName() string {
 	if r.interfaceMonitor == nil {
 	if r.interfaceMonitor == nil {
 		return ""
 		return ""
 	}
 	}
 	return r.interfaceMonitor.DefaultInterfaceName()
 	return r.interfaceMonitor.DefaultInterfaceName()
 }
 }
 
 
-func (r *Router) DefaultInterfaceIndex() int {
+func (r *Router) AutoDetectInterfaceIndex() int {
 	if r.interfaceMonitor == nil {
 	if r.interfaceMonitor == nil {
 		return -1
 		return -1
 	}
 	}

+ 1 - 1
test/go.mod

@@ -34,7 +34,7 @@ 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-20220715031600-dacfbcd606f4 // indirect
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4 // indirect
-	github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361 // indirect
+	github.com/sagernet/sing-tun v0.0.0-20220715065926-9dc73c0bcc1d // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/sirupsen/logrus v1.8.1 // 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

+ 2 - 2
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-20220715031600-dacfbcd606f4 h1:s3yKbPfiNwFFGfcNUs8NcKYzor78HWmZuDl5B0dsVQI=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4 h1:s3yKbPfiNwFFGfcNUs8NcKYzor78HWmZuDl5B0dsVQI=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220715031600-dacfbcd606f4/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
-github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361 h1:M6m9mXG5u151voF0wSDLf5JoDwHU5+4FOMrzb/kaRdc=
-github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo=
+github.com/sagernet/sing-tun v0.0.0-20220715065926-9dc73c0bcc1d h1:wt+OEJA3EiLIjwp+DROTtIXLox+9dwMRsVY/K2MvjXo=
+github.com/sagernet/sing-tun v0.0.0-20220715065926-9dc73c0bcc1d/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo=
 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=