Add route.default_interface option

This commit is contained in:
世界 2022-07-15 11:51:51 +08:00
parent 5a3de62c50
commit 377f3f83a2
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
12 changed files with 45 additions and 23 deletions

View file

@ -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 AutoDetectInterfaceName() string
DefaultInterfaceIndex() int AutoDetectInterfaceIndex() int
} }
type Rule interface { type Rule interface {

View file

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

View file

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

View file

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

View file

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

View file

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

2
go.mod
View file

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

4
go.sum
View file

@ -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-20220715065926-9dc73c0bcc1d h1:wt+OEJA3EiLIjwp+DROTtIXLox+9dwMRsVY/K2MvjXo=
github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo= 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=

View file

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

View file

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

View file

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

View file

@ -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-20220715065926-9dc73c0bcc1d h1:wt+OEJA3EiLIjwp+DROTtIXLox+9dwMRsVY/K2MvjXo=
github.com/sagernet/sing-tun v0.0.0-20220714151007-c0d248af0361/go.mod h1:p7QbUBs2ejf6UQsiHyy1xGAWOk9JWQjZTHy8pOmkWmo= 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=