diff --git a/adapter/router.go b/adapter/router.go index 21a55272..a848618f 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -30,9 +30,10 @@ type Router interface { LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) InterfaceBindManager() control.BindManager + DefaultInterface() string AutoDetectInterface() bool - DefaultInterfaceName() string - DefaultInterfaceIndex() int + AutoDetectInterfaceName() string + AutoDetectInterfaceIndex() int } type Rule interface { diff --git a/common/dialer/auto_linux.go b/common/dialer/auto_linux.go index 52ce6a17..938e7225 100644 --- a/common/dialer/auto_linux.go +++ b/common/dialer/auto_linux.go @@ -10,7 +10,7 @@ import ( func BindToInterface(router adapter.Router) control.Func { return func(network, address string, conn syscall.RawConn) error { - interfaceName := router.DefaultInterfaceName() + interfaceName := router.AutoDetectInterfaceName() if interfaceName == "" { return nil } diff --git a/common/dialer/auto_windows.go b/common/dialer/auto_windows.go index e80d94b5..fe29f0b5 100644 --- a/common/dialer/auto_windows.go +++ b/common/dialer/auto_windows.go @@ -32,7 +32,7 @@ func bind6(handle windows.Handle, ifaceIdx int) error { func BindToInterface(router adapter.Router) control.Func { return func(network, address string, conn syscall.RawConn) error { - interfaceName := router.DefaultInterfaceName() + interfaceName := router.AutoDetectInterfaceName() if interfaceName == "" { return nil } @@ -47,20 +47,20 @@ func BindToInterface(router adapter.Router) control.Func { handle := windows.Handle(fd) // handle ip empty, e.g. net.Listen("udp", ":0") if ipStr == "" { - innerErr = bind4(handle, router.DefaultInterfaceIndex()) + innerErr = bind4(handle, router.AutoDetectInterfaceIndex()) if innerErr != nil { return } // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6 - bind6(handle, router.DefaultInterfaceIndex()) + bind6(handle, router.AutoDetectInterfaceIndex()) return } switch network { case "tcp4", "udp4", "ip4": - innerErr = bind4(handle, router.DefaultInterfaceIndex()) + innerErr = bind4(handle, router.AutoDetectInterfaceIndex()) case "tcp6", "udp6": - innerErr = bind6(handle, router.DefaultInterfaceIndex()) + innerErr = bind6(handle, router.AutoDetectInterfaceIndex()) } }) return E.Errors(innerErr, err) diff --git a/common/dialer/default.go b/common/dialer/default.go index 0d835e48..cde05688 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -29,19 +29,22 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia } else if router.AutoDetectInterface() { if runtime.GOOS == "windows" { 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 { - return router.DefaultInterfaceIndex() + return router.AutoDetectInterfaceIndex() })) } else { 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 { - 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 { dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark)) diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index e640e6b2..75be2b6e 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -47,7 +47,7 @@ Set the default route to the Tun. !!! 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 diff --git a/docs/configuration/route/index.md b/docs/configuration/route/index.md index a6f31ac3..31d34545 100644 --- a/docs/configuration/route/index.md +++ b/docs/configuration/route/index.md @@ -7,7 +7,8 @@ "geosite": {}, "rules": [], "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. -Takes no effect if `outbound.bind_interface` is set. \ No newline at end of file +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. diff --git a/go.mod b/go.mod index 1b903851..3c96ace8 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/sagernet/sing v0.0.0-20220714145306-09b55ce4b6d0 github.com/sagernet/sing-dns v0.0.0-20220711062726-c64e938e4619 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/stretchr/testify v1.8.0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d diff --git a/go.sum b/go.sum index 03ad8d76..c82a84f5 100644 --- a/go.sum +++ b/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-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-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/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/option/route.go b/option/route.go index 5cc9a682..728c5b12 100644 --- a/option/route.go +++ b/option/route.go @@ -14,6 +14,7 @@ type RouteOptions struct { Rules []Rule `json:"rules,omitempty"` Final string `json:"final,omitempty"` AutoDetectInterface bool `json:"auto_detect_interface,omitempty"` + DefaultInterface string `json:"default_interface,omitempty"` } func (o RouteOptions) Equals(other RouteOptions) bool { diff --git a/route/router.go b/route/router.go index 5947f898..64e04af1 100644 --- a/route/router.go +++ b/route/router.go @@ -68,6 +68,7 @@ type Router struct { interfaceBindManager control.BindManager networkMonitor tun.NetworkUpdateMonitor autoDetectInterface bool + defaultInterface string interfaceMonitor tun.DefaultInterfaceMonitor } @@ -89,6 +90,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy), interfaceBindManager: control.NewBindManager(), autoDetectInterface: options.AutoDetectInterface, + defaultInterface: options.DefaultInterface, } for i, ruleOptions := range options.Rules { routeRule, err := NewRule(router, logger, ruleOptions) @@ -540,14 +542,18 @@ func (r *Router) AutoDetectInterface() bool { 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 { return "" } return r.interfaceMonitor.DefaultInterfaceName() } -func (r *Router) DefaultInterfaceIndex() int { +func (r *Router) AutoDetectInterfaceIndex() int { if r.interfaceMonitor == nil { return -1 } diff --git a/test/go.mod b/test/go.mod index cf0a5603..e71fad2d 100644 --- a/test/go.mod +++ b/test/go.mod @@ -34,7 +34,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // 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-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/vishvananda/netlink v1.1.0 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect diff --git a/test/go.sum b/test/go.sum index 4fcb3b4d..6b37b78d 100644 --- a/test/go.sum +++ b/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-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-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.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=