mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-12-04 20:36:37 +00:00
Refactor multi networks strategy
This commit is contained in:
parent
8272ad3ff3
commit
f3c9066ca1
|
@ -69,6 +69,8 @@ type InboundContext struct {
|
||||||
UDPDisableDomainUnmapping bool
|
UDPDisableDomainUnmapping bool
|
||||||
UDPConnect bool
|
UDPConnect bool
|
||||||
NetworkStrategy C.NetworkStrategy
|
NetworkStrategy C.NetworkStrategy
|
||||||
|
NetworkType []C.InterfaceType
|
||||||
|
FallbackNetworkType []C.InterfaceType
|
||||||
FallbackDelay time.Duration
|
FallbackDelay time.Duration
|
||||||
|
|
||||||
DNSServer string
|
DNSServer string
|
||||||
|
|
|
@ -28,10 +28,12 @@ type NetworkManager interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkOptions struct {
|
type NetworkOptions struct {
|
||||||
DefaultNetworkStrategy C.NetworkStrategy
|
NetworkStrategy C.NetworkStrategy
|
||||||
DefaultFallbackDelay time.Duration
|
NetworkType []C.InterfaceType
|
||||||
DefaultInterface string
|
FallbackNetworkType []C.InterfaceType
|
||||||
DefaultMark uint32
|
FallbackDelay time.Duration
|
||||||
|
BindInterface string
|
||||||
|
RoutingMark uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type InterfaceUpdateListener interface {
|
type InterfaceUpdateListener interface {
|
||||||
|
@ -45,7 +47,7 @@ type WIFIState struct {
|
||||||
|
|
||||||
type NetworkInterface struct {
|
type NetworkInterface struct {
|
||||||
control.Interface
|
control.Interface
|
||||||
Type string
|
Type C.InterfaceType
|
||||||
DNSServers []string
|
DNSServers []string
|
||||||
Expensive bool
|
Expensive bool
|
||||||
Constrained bool
|
Constrained bool
|
||||||
|
|
|
@ -26,7 +26,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
|
||||||
var err error
|
var err error
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
if len(metadata.DestinationAddresses) > 0 {
|
||||||
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
||||||
outConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.FallbackDelay)
|
outConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
||||||
} else {
|
} else {
|
||||||
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
outConn, err = N.DialSerial(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
|
||||||
if metadata.UDPConnect {
|
if metadata.UDPConnect {
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
if len(metadata.DestinationAddresses) > 0 {
|
||||||
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
||||||
outConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.FallbackDelay)
|
outConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
||||||
} else {
|
} else {
|
||||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn,
|
||||||
} else {
|
} else {
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
if len(metadata.DestinationAddresses) > 0 {
|
||||||
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
||||||
outPacketConn, destinationAddress, err = dialer.ListenSerialNetworkPacket(ctx, parallelDialer, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.FallbackDelay)
|
outPacketConn, destinationAddress, err = dialer.ListenSerialNetworkPacket(ctx, parallelDialer, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
||||||
} else {
|
} else {
|
||||||
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/common/conntrack"
|
"github.com/sagernet/sing-box/common/conntrack"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/atomic"
|
"github.com/sagernet/sing/common/atomic"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
@ -33,6 +34,8 @@ type DefaultDialer struct {
|
||||||
isWireGuardListener bool
|
isWireGuardListener bool
|
||||||
networkManager adapter.NetworkManager
|
networkManager adapter.NetworkManager
|
||||||
networkStrategy C.NetworkStrategy
|
networkStrategy C.NetworkStrategy
|
||||||
|
networkType []C.InterfaceType
|
||||||
|
fallbackNetworkType []C.InterfaceType
|
||||||
networkFallbackDelay time.Duration
|
networkFallbackDelay time.Duration
|
||||||
networkLastFallback atomic.TypedValue[time.Time]
|
networkLastFallback atomic.TypedValue[time.Time]
|
||||||
}
|
}
|
||||||
|
@ -43,6 +46,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
||||||
listener net.ListenConfig
|
listener net.ListenConfig
|
||||||
interfaceFinder control.InterfaceFinder
|
interfaceFinder control.InterfaceFinder
|
||||||
networkStrategy C.NetworkStrategy
|
networkStrategy C.NetworkStrategy
|
||||||
|
networkType []C.InterfaceType
|
||||||
|
fallbackNetworkType []C.InterfaceType
|
||||||
networkFallbackDelay time.Duration
|
networkFallbackDelay time.Duration
|
||||||
)
|
)
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
|
@ -56,8 +61,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
}
|
}
|
||||||
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(uint32(options.RoutingMark)))
|
||||||
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
|
listener.Control = control.Append(listener.Control, control.RoutingMark(uint32(options.RoutingMark)))
|
||||||
}
|
}
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
autoRedirectOutputMark := networkManager.AutoRedirectOutputMark()
|
autoRedirectOutputMark := networkManager.AutoRedirectOutputMark()
|
||||||
|
@ -74,6 +79,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
||||||
return nil, E.New("`network_strategy` is conflict with `bind_interface`, `inet4_bind_address` and `inet6_bind_address`")
|
return nil, E.New("`network_strategy` is conflict with `bind_interface`, `inet4_bind_address` and `inet6_bind_address`")
|
||||||
}
|
}
|
||||||
networkStrategy = C.NetworkStrategy(options.NetworkStrategy)
|
networkStrategy = C.NetworkStrategy(options.NetworkStrategy)
|
||||||
|
networkType = common.Map(options.NetworkType, option.InterfaceType.Build)
|
||||||
|
fallbackNetworkType = common.Map(options.FallbackNetworkType, option.InterfaceType.Build)
|
||||||
networkFallbackDelay = time.Duration(options.NetworkFallbackDelay)
|
networkFallbackDelay = time.Duration(options.NetworkFallbackDelay)
|
||||||
if networkManager == nil || !networkManager.AutoDetectInterface() {
|
if networkManager == nil || !networkManager.AutoDetectInterface() {
|
||||||
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
|
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
|
||||||
|
@ -81,14 +88,17 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
||||||
}
|
}
|
||||||
if networkManager != nil && options.BindInterface == "" && options.Inet4BindAddress == nil && options.Inet6BindAddress == nil {
|
if networkManager != nil && options.BindInterface == "" && options.Inet4BindAddress == nil && options.Inet6BindAddress == nil {
|
||||||
defaultOptions := networkManager.DefaultOptions()
|
defaultOptions := networkManager.DefaultOptions()
|
||||||
if defaultOptions.DefaultInterface != "" {
|
if options.BindInterface == "" {
|
||||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.DefaultInterface, -1)
|
if defaultOptions.BindInterface != "" {
|
||||||
|
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.BindInterface, -1)
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
} else if networkManager.AutoDetectInterface() {
|
} else if networkManager.AutoDetectInterface() {
|
||||||
if defaultOptions.DefaultNetworkStrategy != C.NetworkStrategyDefault && C.NetworkStrategy(options.NetworkStrategy) == C.NetworkStrategyDefault {
|
if defaultOptions.NetworkStrategy != C.NetworkStrategyDefault && C.NetworkStrategy(options.NetworkStrategy) == C.NetworkStrategyDefault {
|
||||||
networkStrategy = defaultOptions.DefaultNetworkStrategy
|
networkStrategy = defaultOptions.NetworkStrategy
|
||||||
networkFallbackDelay = defaultOptions.DefaultFallbackDelay
|
networkType = defaultOptions.NetworkType
|
||||||
|
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
||||||
|
networkFallbackDelay = defaultOptions.FallbackDelay
|
||||||
bindFunc := networkManager.ProtectFunc()
|
bindFunc := networkManager.ProtectFunc()
|
||||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||||
listener.Control = control.Append(listener.Control, bindFunc)
|
listener.Control = control.Append(listener.Control, bindFunc)
|
||||||
|
@ -99,6 +109,11 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.RoutingMark == 0 && defaultOptions.RoutingMark != 0 {
|
||||||
|
dialer.Control = control.Append(dialer.Control, control.RoutingMark(defaultOptions.RoutingMark))
|
||||||
|
listener.Control = control.Append(listener.Control, control.RoutingMark(defaultOptions.RoutingMark))
|
||||||
|
}
|
||||||
|
}
|
||||||
if options.ReuseAddr {
|
if options.ReuseAddr {
|
||||||
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
listener.Control = control.Append(listener.Control, control.ReuseAddr())
|
||||||
}
|
}
|
||||||
|
@ -179,6 +194,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
||||||
isWireGuardListener: options.IsWireGuardListener,
|
isWireGuardListener: options.IsWireGuardListener,
|
||||||
networkManager: networkManager,
|
networkManager: networkManager,
|
||||||
networkStrategy: networkStrategy,
|
networkStrategy: networkStrategy,
|
||||||
|
networkType: networkType,
|
||||||
|
fallbackNetworkType: fallbackNetworkType,
|
||||||
networkFallbackDelay: networkFallbackDelay,
|
networkFallbackDelay: networkFallbackDelay,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -202,11 +219,11 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
||||||
return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkFallbackDelay)
|
return d.DialParallelInterface(ctx, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network string, address M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error) {
|
func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network string, address M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
if strategy == C.NetworkStrategyDefault {
|
if strategy == C.NetworkStrategyDefault {
|
||||||
return d.DialContext(ctx, network, address)
|
return d.DialContext(ctx, network, address)
|
||||||
}
|
}
|
||||||
|
@ -226,9 +243,9 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if !fastFallback {
|
if !fastFallback {
|
||||||
conn, isPrimary, err = d.dialParallelInterface(ctx, dialer, network, address.String(), strategy, fallbackDelay)
|
conn, isPrimary, err = d.dialParallelInterface(ctx, dialer, network, address.String(), strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
} else {
|
} else {
|
||||||
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), strategy, fallbackDelay, d.networkLastFallback.Store)
|
conn, isPrimary, err = d.dialParallelInterfaceFastFallback(ctx, dialer, network, address.String(), strategy, interfaceType, fallbackInterfaceType, fallbackDelay, d.networkLastFallback.Store)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -249,11 +266,11 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
||||||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkFallbackDelay)
|
return d.ListenSerialInterfacePacket(ctx, destination, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||||
if strategy == C.NetworkStrategyDefault {
|
if strategy == C.NetworkStrategyDefault {
|
||||||
return d.ListenPacket(ctx, destination)
|
return d.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
@ -264,11 +281,11 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
||||||
if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||||
network += "4"
|
network += "4"
|
||||||
}
|
}
|
||||||
return trackPacketConn(d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", strategy, fallbackDelay))
|
return trackPacketConn(d.listenSerialInterfacePacket(ctx, d.udpListener, network, "", strategy, interfaceType, fallbackInterfaceType, fallbackDelay))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
|
||||||
return d.listenSerialInterfacePacket(context.Background(), d.udpListener, network, address, d.networkStrategy, d.networkFallbackDelay)
|
return d.listenSerialInterfacePacket(context.Background(), d.udpListener, network, address, d.networkStrategy, d.networkType, d.fallbackNetworkType, d.networkFallbackDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
func trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||||
|
|
|
@ -7,14 +7,14 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Dialer, network string, addr string, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, bool, error) {
|
func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Dialer, network string, addr string, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, bool, error) {
|
||||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy)
|
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy, interfaceType, fallbackInterfaceType)
|
||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, false, E.New("no available network interface")
|
return nil, false, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ func (d *DefaultDialer) dialParallelInterface(ctx context.Context, dialer net.Di
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, dialer net.Dialer, network string, addr string, strategy C.NetworkStrategy, fallbackDelay time.Duration, resetFastFallback func(time.Time)) (net.Conn, bool, error) {
|
func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, dialer net.Dialer, network string, addr string, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration, resetFastFallback func(time.Time)) (net.Conn, bool, error) {
|
||||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy)
|
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy, interfaceType, fallbackInterfaceType)
|
||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, false, E.New("no available network interface")
|
return nil, false, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
|
@ -144,8 +144,8 @@ func (d *DefaultDialer) dialParallelInterfaceFastFallback(ctx context.Context, d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listener net.ListenConfig, network string, addr string, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, error) {
|
func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listener net.ListenConfig, network string, addr string, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy)
|
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy, interfaceType, fallbackInterfaceType)
|
||||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||||
return nil, E.New("no available network interface")
|
return nil, E.New("no available network interface")
|
||||||
}
|
}
|
||||||
|
@ -174,12 +174,12 @@ func (d *DefaultDialer) listenSerialInterfacePacket(ctx context.Context, listene
|
||||||
return nil, E.Errors(errors...)
|
return nil, E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkStrategy) (primaryInterfaces []adapter.NetworkInterface, fallbackInterfaces []adapter.NetworkInterface) {
|
func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType) (primaryInterfaces []adapter.NetworkInterface, fallbackInterfaces []adapter.NetworkInterface) {
|
||||||
interfaces := networkManager.NetworkInterfaces()
|
interfaces := networkManager.NetworkInterfaces()
|
||||||
switch strategy {
|
switch strategy {
|
||||||
case C.NetworkStrategyFallback:
|
case C.NetworkStrategyDefault:
|
||||||
|
if len(interfaceType) == 0 {
|
||||||
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||||
if defaultIf != nil {
|
|
||||||
for _, iif := range interfaces {
|
for _, iif := range interfaces {
|
||||||
if iif.Index == defaultIf.Index {
|
if iif.Index == defaultIf.Index {
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
primaryInterfaces = append(primaryInterfaces, iif)
|
||||||
|
@ -188,54 +188,36 @@ func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
primaryInterfaces = interfaces
|
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||||
|
return common.Contains(interfaceType, iif.Type)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
case C.NetworkStrategyHybrid:
|
case C.NetworkStrategyHybrid:
|
||||||
|
if len(interfaceType) == 0 {
|
||||||
primaryInterfaces = interfaces
|
primaryInterfaces = interfaces
|
||||||
case C.NetworkStrategyWIFI:
|
} else {
|
||||||
|
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||||
|
return common.Contains(interfaceType, iif.Type)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case C.NetworkStrategyFallback:
|
||||||
|
if len(interfaceType) == 0 {
|
||||||
|
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||||
for _, iif := range interfaces {
|
for _, iif := range interfaces {
|
||||||
if iif.Type == C.InterfaceTypeWIFI {
|
if iif.Index == defaultIf.Index {
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
primaryInterfaces = append(primaryInterfaces, iif)
|
||||||
} else {
|
} else {
|
||||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
fallbackInterfaces = append(fallbackInterfaces, iif)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case C.NetworkStrategyCellular:
|
|
||||||
for _, iif := range interfaces {
|
|
||||||
if iif.Type == C.InterfaceTypeCellular {
|
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
|
||||||
} else {
|
} else {
|
||||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||||
|
return common.Contains(interfaceType, iif.Type)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
fallbackInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||||
case C.NetworkStrategyEthernet:
|
return common.Contains(fallbackInterfaceType, iif.Type)
|
||||||
for _, iif := range interfaces {
|
})
|
||||||
if iif.Type == C.InterfaceTypeEthernet {
|
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
|
||||||
} else {
|
|
||||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case C.NetworkStrategyWIFIOnly:
|
|
||||||
for _, iif := range interfaces {
|
|
||||||
if iif.Type == C.InterfaceTypeWIFI {
|
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case C.NetworkStrategyCellularOnly:
|
|
||||||
for _, iif := range interfaces {
|
|
||||||
if iif.Type == C.InterfaceTypeCellular {
|
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case C.NetworkStrategyEthernetOnly:
|
|
||||||
for _, iif := range interfaces {
|
|
||||||
if iif.Type == C.InterfaceTypeEthernet {
|
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic(F.ToString("unknown network strategy: ", strategy))
|
|
||||||
}
|
}
|
||||||
return primaryInterfaces, fallbackInterfaces
|
return primaryInterfaces, fallbackInterfaces
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,13 @@ import (
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DialSerialNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error) {
|
func DialSerialNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel {
|
if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel {
|
||||||
return parallelDialer.DialParallelNetwork(ctx, network, destination, destinationAddresses, strategy, fallbackDelay)
|
return parallelDialer.DialParallelNetwork(ctx, network, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
}
|
}
|
||||||
var errors []error
|
var errors []error
|
||||||
for _, address := range destinationAddresses {
|
for _, address := range destinationAddresses {
|
||||||
conn, err := dialer.DialParallelInterface(ctx, network, M.SocksaddrFrom(address, destination.Port), strategy, fallbackDelay)
|
conn, err := dialer.DialParallelInterface(ctx, network, M.SocksaddrFrom(address, destination.Port), strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func DialSerialNetwork(ctx context.Context, dialer ParallelInterfaceDialer, netw
|
||||||
return nil, E.Errors(errors...)
|
return nil, E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, preferIPv6 bool, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error) {
|
func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, preferIPv6 bool, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
if fallbackDelay == 0 {
|
if fallbackDelay == 0 {
|
||||||
fallbackDelay = N.DefaultFallbackDelay
|
fallbackDelay = N.DefaultFallbackDelay
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, ne
|
||||||
return address.Is6() && !address.Is4In6()
|
return address.Is6() && !address.Is4In6()
|
||||||
})
|
})
|
||||||
if len(addresses4) == 0 || len(addresses6) == 0 {
|
if len(addresses4) == 0 || len(addresses6) == 0 {
|
||||||
return DialSerialNetwork(ctx, dialer, network, destination, destinationAddresses, strategy, fallbackDelay)
|
return DialSerialNetwork(ctx, dialer, network, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
}
|
}
|
||||||
var primaries, fallbacks []netip.Addr
|
var primaries, fallbacks []netip.Addr
|
||||||
if preferIPv6 {
|
if preferIPv6 {
|
||||||
|
@ -65,7 +65,7 @@ func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, ne
|
||||||
if !primary {
|
if !primary {
|
||||||
ras = fallbacks
|
ras = fallbacks
|
||||||
}
|
}
|
||||||
c, err := DialSerialNetwork(ctx, dialer, network, destination, ras, strategy, fallbackDelay)
|
c, err := DialSerialNetwork(ctx, dialer, network, destination, ras, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
select {
|
select {
|
||||||
case results <- dialResult{Conn: c, error: err, primary: primary, done: true}:
|
case results <- dialResult{Conn: c, error: err, primary: primary, done: true}:
|
||||||
case <-returned:
|
case <-returned:
|
||||||
|
@ -106,13 +106,13 @@ func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, ne
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListenSerialNetworkPacket(ctx context.Context, dialer ParallelInterfaceDialer, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
func ListenSerialNetworkPacket(ctx context.Context, dialer ParallelInterfaceDialer, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||||
if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel {
|
if parallelDialer, isParallel := dialer.(ParallelNetworkDialer); isParallel {
|
||||||
return parallelDialer.ListenSerialNetworkPacket(ctx, destination, destinationAddresses, strategy, fallbackDelay)
|
return parallelDialer.ListenSerialNetworkPacket(ctx, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
}
|
}
|
||||||
var errors []error
|
var errors []error
|
||||||
for _, address := range destinationAddresses {
|
for _, address := range destinationAddresses {
|
||||||
conn, err := dialer.ListenSerialInterfacePacket(ctx, M.SocksaddrFrom(address, destination.Port), strategy, fallbackDelay)
|
conn, err := dialer.ListenSerialInterfacePacket(ctx, M.SocksaddrFrom(address, destination.Port), strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return conn, address, nil
|
return conn, address, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,11 +77,11 @@ func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInter
|
||||||
|
|
||||||
type ParallelInterfaceDialer interface {
|
type ParallelInterfaceDialer interface {
|
||||||
N.Dialer
|
N.Dialer
|
||||||
DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error)
|
DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||||
ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, error)
|
ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParallelNetworkDialer interface {
|
type ParallelNetworkDialer interface {
|
||||||
DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error)
|
DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error)
|
||||||
ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error)
|
ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
||||||
return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error) {
|
func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
if !destination.IsFqdn() {
|
if !destination.IsFqdn() {
|
||||||
return d.dialer.DialContext(ctx, network, destination)
|
return d.dialer.DialContext(ctx, network, destination)
|
||||||
}
|
}
|
||||||
|
@ -128,13 +128,13 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context
|
||||||
fallbackDelay = d.fallbackDelay
|
fallbackDelay = d.fallbackDelay
|
||||||
}
|
}
|
||||||
if d.parallel {
|
if d.parallel {
|
||||||
return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, strategy, fallbackDelay)
|
return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.strategy == dns.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
} else {
|
} else {
|
||||||
return DialSerialNetwork(ctx, d.dialer, network, destination, addresses, strategy, fallbackDelay)
|
return DialSerialNetwork(ctx, d.dialer, network, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, error) {
|
func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error) {
|
||||||
if !destination.IsFqdn() {
|
if !destination.IsFqdn() {
|
||||||
return d.dialer.ListenPacket(ctx, destination)
|
return d.dialer.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conn, destinationAddress, err := ListenSerialNetworkPacket(ctx, d.dialer, destination, addresses, strategy, fallbackDelay)
|
conn, destinationAddress, err := ListenSerialNetworkPacket(ctx, d.dialer, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,7 +226,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||||
}
|
}
|
||||||
rule.AdGuardDomainMatcher = matcher
|
rule.AdGuardDomainMatcher = matcher
|
||||||
case ruleItemNetworkType:
|
case ruleItemNetworkType:
|
||||||
rule.NetworkType, err = readRuleItemString(reader)
|
rule.NetworkType, err = readRuleItemUint8[option.InterfaceType](reader)
|
||||||
case ruleItemNetworkIsExpensive:
|
case ruleItemNetworkIsExpensive:
|
||||||
rule.NetworkIsExpensive = true
|
rule.NetworkIsExpensive = true
|
||||||
case ruleItemNetworkIsConstrained:
|
case ruleItemNetworkIsConstrained:
|
||||||
|
@ -349,7 +349,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||||
if generateVersion < C.RuleSetVersion3 {
|
if generateVersion < C.RuleSetVersion3 {
|
||||||
return E.New("network_type rule item is only supported in version 3 or later")
|
return E.New("network_type rule item is only supported in version 3 or later")
|
||||||
}
|
}
|
||||||
err = writeRuleItemString(writer, ruleItemNetworkType, rule.NetworkType)
|
err = writeRuleItemUint8(writer, ruleItemNetworkType, rule.NetworkType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -414,6 +414,18 @@ func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) e
|
||||||
return varbin.Write(writer, binary.BigEndian, value)
|
return varbin.Write(writer, binary.BigEndian, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readRuleItemUint8[E ~uint8](reader varbin.Reader) ([]E, error) {
|
||||||
|
return varbin.ReadValue[[]E](reader, binary.BigEndian)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeRuleItemUint8[E ~uint8](writer varbin.Writer, itemType uint8, value []E) error {
|
||||||
|
err := writer.WriteByte(itemType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return varbin.Write(writer, binary.BigEndian, value)
|
||||||
|
}
|
||||||
|
|
||||||
func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
|
func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
|
||||||
return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
|
return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,44 +5,52 @@ import (
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type InterfaceType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
InterfaceTypeWIFI = "wifi"
|
InterfaceTypeWIFI InterfaceType = iota
|
||||||
InterfaceTypeCellular = "cellular"
|
InterfaceTypeCellular
|
||||||
InterfaceTypeEthernet = "ethernet"
|
InterfaceTypeEthernet
|
||||||
InterfaceTypeOther = "other"
|
InterfaceTypeOther
|
||||||
)
|
)
|
||||||
|
|
||||||
type NetworkStrategy int
|
var (
|
||||||
|
interfaceTypeToString = map[InterfaceType]string{
|
||||||
|
InterfaceTypeWIFI: "wifi",
|
||||||
|
InterfaceTypeCellular: "cellular",
|
||||||
|
InterfaceTypeEthernet: "ethernet",
|
||||||
|
InterfaceTypeOther: "other",
|
||||||
|
}
|
||||||
|
StringToInterfaceType = common.ReverseMap(interfaceTypeToString)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t InterfaceType) String() string {
|
||||||
|
name, loaded := interfaceTypeToString[t]
|
||||||
|
if !loaded {
|
||||||
|
return F.ToString(int(t))
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkStrategy uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NetworkStrategyDefault NetworkStrategy = iota
|
NetworkStrategyDefault NetworkStrategy = iota
|
||||||
NetworkStrategyFallback
|
NetworkStrategyFallback
|
||||||
NetworkStrategyHybrid
|
NetworkStrategyHybrid
|
||||||
NetworkStrategyWIFI
|
|
||||||
NetworkStrategyCellular
|
|
||||||
NetworkStrategyEthernet
|
|
||||||
NetworkStrategyWIFIOnly
|
|
||||||
NetworkStrategyCellularOnly
|
|
||||||
NetworkStrategyEthernetOnly
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
NetworkStrategyToString = map[NetworkStrategy]string{
|
networkStrategyToString = map[NetworkStrategy]string{
|
||||||
NetworkStrategyDefault: "default",
|
NetworkStrategyDefault: "default",
|
||||||
NetworkStrategyFallback: "fallback",
|
NetworkStrategyFallback: "fallback",
|
||||||
NetworkStrategyHybrid: "hybrid",
|
NetworkStrategyHybrid: "hybrid",
|
||||||
NetworkStrategyWIFI: "wifi",
|
|
||||||
NetworkStrategyCellular: "cellular",
|
|
||||||
NetworkStrategyEthernet: "ethernet",
|
|
||||||
NetworkStrategyWIFIOnly: "wifi_only",
|
|
||||||
NetworkStrategyCellularOnly: "cellular_only",
|
|
||||||
NetworkStrategyEthernetOnly: "ethernet_only",
|
|
||||||
}
|
}
|
||||||
StringToNetworkStrategy = common.ReverseMap(NetworkStrategyToString)
|
StringToNetworkStrategy = common.ReverseMap(networkStrategyToString)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s NetworkStrategy) String() string {
|
func (s NetworkStrategy) String() string {
|
||||||
name, loaded := NetworkStrategyToString[s]
|
name, loaded := networkStrategyToString[s]
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return F.ToString(int(s))
|
return F.ToString(int(s))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ icon: material/new-box
|
||||||
!!! quote "Changes in sing-box 1.11.0"
|
!!! quote "Changes in sing-box 1.11.0"
|
||||||
|
|
||||||
:material-plus: [default_network_strategy](#default_network_strategy)
|
:material-plus: [default_network_strategy](#default_network_strategy)
|
||||||
|
:material-plus: [default_network_type](#default_network_type)
|
||||||
|
:material-plus: [default_fallback_network_type](#default_fallback_network_type)
|
||||||
:material-alert: [default_fallback_delay](#default_fallback_delay)
|
:material-alert: [default_fallback_delay](#default_fallback_delay)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
@ -30,17 +32,18 @@ icon: material/new-box
|
||||||
"default_interface": "",
|
"default_interface": "",
|
||||||
"default_mark": 0,
|
"default_mark": 0,
|
||||||
"default_network_strategy": "",
|
"default_network_strategy": "",
|
||||||
|
"default_network_type": [],
|
||||||
|
"default_fallback_network_type": [],
|
||||||
"default_fallback_delay": ""
|
"default_fallback_delay": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fields
|
!!! note ""
|
||||||
|
|
||||||
| Key | Format |
|
You can ignore the JSON Array [] tag when the content is only one item
|
||||||
|-----------|-----------------------|
|
|
||||||
| `geoip` | [GeoIP](./geoip/) |
|
### Fields
|
||||||
| `geosite` | [Geosite](./geosite/) |
|
|
||||||
|
|
||||||
#### rules
|
#### rules
|
||||||
|
|
||||||
|
@ -96,11 +99,9 @@ Takes no effect if `outbound.routing_mark` is set.
|
||||||
|
|
||||||
#### default_network_strategy
|
#### default_network_strategy
|
||||||
|
|
||||||
!!! quote ""
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS with `auto_detect_interface` enabled.
|
See [Dial Fields](/configuration/shared/dial/#network_strategy) for details.
|
||||||
|
|
||||||
Strategy for selecting network interfaces.
|
|
||||||
|
|
||||||
Takes no effect if `outbound.bind_interface`, `outbound.inet4_bind_address` or `outbound.inet6_bind_address` is set.
|
Takes no effect if `outbound.bind_interface`, `outbound.inet4_bind_address` or `outbound.inet6_bind_address` is set.
|
||||||
|
|
||||||
|
@ -108,12 +109,20 @@ Can be overrides by `outbound.network_strategy`.
|
||||||
|
|
||||||
Conflicts with `default_interface`.
|
Conflicts with `default_interface`.
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/#network_strategy) for available values.
|
#### default_network_type
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#network_type) for details.
|
||||||
|
|
||||||
|
#### default_fallback_network_type
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#fallback_network_type) for details.
|
||||||
|
|
||||||
#### default_fallback_delay
|
#### default_fallback_delay
|
||||||
|
|
||||||
!!! quote ""
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS with `auto_detect_interface` enabled and `network_strategy` set.
|
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/#fallback_delay) for details.
|
See [Dial Fields](/configuration/shared/dial/#fallback_delay) for details.
|
|
@ -1,5 +1,16 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
# 路由
|
# 路由
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.11.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [network_strategy](#network_strategy)
|
||||||
|
:material-plus: [default_network_type](#default_network_type)
|
||||||
|
:material-plus: [default_fallback_network_type](#default_fallback_network_type)
|
||||||
|
:material-alert: [default_fallback_delay](#default_fallback_delay)
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
:material-plus: [rule_set](#rule_set)
|
:material-plus: [rule_set](#rule_set)
|
||||||
|
@ -26,6 +37,10 @@
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
|
||||||
| 键 | 格式 |
|
| 键 | 格式 |
|
||||||
|
@ -87,11 +102,9 @@
|
||||||
|
|
||||||
#### network_strategy
|
#### network_strategy
|
||||||
|
|
||||||
!!! quote ""
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端中支持,并且需要 `auto_detect_interface`。
|
详情参阅 [拨号字段](/configuration/shared/dial/#network_strategy)。
|
||||||
|
|
||||||
选择网络接口的策略。
|
|
||||||
|
|
||||||
当 `outbound.bind_interface`, `outbound.inet4_bind_address` 或 `outbound.inet6_bind_address` 已设置时不生效。
|
当 `outbound.bind_interface`, `outbound.inet4_bind_address` 或 `outbound.inet6_bind_address` 已设置时不生效。
|
||||||
|
|
||||||
|
@ -99,12 +112,20 @@
|
||||||
|
|
||||||
与 `default_interface` 冲突。
|
与 `default_interface` 冲突。
|
||||||
|
|
||||||
可用值请参阅 [拨号字段](/configuration/shared/dial/#network_strategy)。
|
#### default_network_type
|
||||||
|
|
||||||
#### fallback_delay
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|
||||||
!!! quote ""
|
详情参阅 [拨号字段](/configuration/shared/dial/#default_network_type)。
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端中支持,并且需要 `auto_detect_interface` 且 `network_strategy` 已设置。
|
#### default_fallback_network_type
|
||||||
|
|
||||||
详情请参阅 [拨号字段](/configuration/shared/dial/#fallback_delay)。
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|
||||||
|
详情参阅 [拨号字段](/configuration/shared/dial/#default_fallback_network_type)。
|
||||||
|
|
||||||
|
#### default_fallback_delay
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|
||||||
|
详情参阅 [拨号字段](/configuration/shared/dial/#fallback_delay)。
|
||||||
|
|
|
@ -11,12 +11,18 @@ icon: material/new-box
|
||||||
"action": "route", // default
|
"action": "route", // default
|
||||||
"outbound": "",
|
"outbound": "",
|
||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
|
"network_type": [],
|
||||||
|
"fallback_network_type": [],
|
||||||
"fallback_delay": "",
|
"fallback_delay": "",
|
||||||
"udp_disable_domain_unmapping": false,
|
"udp_disable_domain_unmapping": false,
|
||||||
"udp_connect": false
|
"udp_connect": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
You can ignore the JSON Array [] tag when the content is only one item
|
||||||
|
|
||||||
`route` inherits the classic rule behavior of routing connection to the specified outbound.
|
`route` inherits the classic rule behavior of routing connection to the specified outbound.
|
||||||
|
|
||||||
#### outbound
|
#### outbound
|
||||||
|
@ -27,23 +33,21 @@ Tag of target outbound.
|
||||||
|
|
||||||
#### network_strategy
|
#### network_strategy
|
||||||
|
|
||||||
!!! quote ""
|
See [Dial Fields](/configuration/shared/dial/#network_strategy) for details.
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS with `auto_detect_interface` enabled.
|
|
||||||
|
|
||||||
Strategy for selecting network interfaces.
|
|
||||||
|
|
||||||
Only take effect if outbound is direct without `outbound.bind_interface`,
|
Only take effect if outbound is direct without `outbound.bind_interface`,
|
||||||
`outbound.inet4_bind_address` and `outbound.inet6_bind_address` set.
|
`outbound.inet4_bind_address` and `outbound.inet6_bind_address` set.
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/#network_strategy) for available values.
|
#### network_type
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#network_type) for details.
|
||||||
|
|
||||||
|
#### fallback_network_type
|
||||||
|
|
||||||
|
See [Dial Fields](/configuration/shared/dial/#fallback_network_type) for details.
|
||||||
|
|
||||||
#### fallback_delay
|
#### fallback_delay
|
||||||
|
|
||||||
!!! quote ""
|
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS with `auto_detect_interface` enabled and `network_strategy` set.
|
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial/#fallback_delay) for details.
|
See [Dial Fields](/configuration/shared/dial/#fallback_delay) for details.
|
||||||
|
|
||||||
#### udp_disable_domain_unmapping
|
#### udp_disable_domain_unmapping
|
||||||
|
|
|
@ -12,6 +12,8 @@ icon: material/new-box
|
||||||
"outbound": "",
|
"outbound": "",
|
||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
"fallback_delay": "",
|
"fallback_delay": "",
|
||||||
|
"network_type": [],
|
||||||
|
"fallback_network_type": [],
|
||||||
"udp_disable_domain_unmapping": false,
|
"udp_disable_domain_unmapping": false,
|
||||||
"udp_connect": false
|
"udp_connect": false
|
||||||
}
|
}
|
||||||
|
@ -27,23 +29,21 @@ icon: material/new-box
|
||||||
|
|
||||||
#### network_strategy
|
#### network_strategy
|
||||||
|
|
||||||
!!! quote ""
|
详情参阅 [拨号字段](/configuration/shared/dial/#network_strategy)。
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端中支持,并且需要 `auto_detect_interface`。
|
|
||||||
|
|
||||||
选择网络接口的策略。
|
|
||||||
|
|
||||||
仅当出站为 `direct` 且 `outbound.bind_interface`, `outbound.inet4_bind_address`
|
仅当出站为 `direct` 且 `outbound.bind_interface`, `outbound.inet4_bind_address`
|
||||||
且 `outbound.inet6_bind_address` 未设置时生效。
|
且 `outbound.inet6_bind_address` 未设置时生效。
|
||||||
|
|
||||||
可用值参阅 [拨号字段](/configuration/shared/dial/#network_strategy)。
|
#### network_type
|
||||||
|
|
||||||
|
详情参阅 [拨号字段](/configuration/shared/dial/#network_type)。
|
||||||
|
|
||||||
|
#### fallback_network_type
|
||||||
|
|
||||||
|
详情参阅 [拨号字段](/configuration/shared/dial/#fallback_network_type)。
|
||||||
|
|
||||||
#### fallback_delay
|
#### fallback_delay
|
||||||
|
|
||||||
!!! quote ""
|
|
||||||
|
|
||||||
仅在 Android 与 Apple 平台图形客户端中支持,并且需要 `auto_detect_interface` 且 `network_strategy` 已设置。
|
|
||||||
|
|
||||||
详情参阅 [拨号字段](/configuration/shared/dial/#fallback_delay)。
|
详情参阅 [拨号字段](/configuration/shared/dial/#fallback_delay)。
|
||||||
|
|
||||||
#### udp_disable_domain_unmapping
|
#### udp_disable_domain_unmapping
|
||||||
|
@ -68,6 +68,10 @@ icon: material/new-box
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||||
|
|
||||||
`route-options` 为路由设置选项。
|
`route-options` 为路由设置选项。
|
||||||
|
|
||||||
### reject
|
### reject
|
||||||
|
|
|
@ -6,6 +6,8 @@ icon: material/new-box
|
||||||
|
|
||||||
:material-plus: [network_strategy](#network_strategy)
|
:material-plus: [network_strategy](#network_strategy)
|
||||||
:material-alert: [fallback_delay](#fallback_delay)
|
:material-alert: [fallback_delay](#fallback_delay)
|
||||||
|
:material-alert: [network_type](#network_type)
|
||||||
|
:material-alert: [fallback_network_type](#fallback_network_type)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
|
@ -23,10 +25,16 @@ icon: material/new-box
|
||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
"domain_strategy": "prefer_ipv6",
|
"domain_strategy": "prefer_ipv6",
|
||||||
"network_strategy": "default",
|
"network_strategy": "default",
|
||||||
|
"network_type": [],
|
||||||
|
"fallback_network_type": [],
|
||||||
"fallback_delay": "300ms"
|
"fallback_delay": "300ms"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
You can ignore the JSON Array [] tag when the content is only one item
|
||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
|
||||||
#### detour
|
#### detour
|
||||||
|
@ -101,30 +109,57 @@ If set, the requested domain name will be resolved to IP before connect.
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS with `auto_detect_interface` enabled.
|
Only supported in graphical clients on Android and Apple platforms with `auto_detect_interface` enabled.
|
||||||
|
|
||||||
Strategy for selecting network interfaces.
|
Strategy for selecting network interfaces.
|
||||||
|
|
||||||
Available values:
|
Available values:
|
||||||
|
|
||||||
- `default` (default): Connect to the default interface.
|
- `default` (default): Connect to default network or networks specified in `network_type` sequentially.
|
||||||
- `fallback`: Try all other interfaces when timeout.
|
- `hybrid`: Connect to all networks or networks specified in `network_type` concurrently.
|
||||||
- `hybrid`: Connect to all interfaces concurrently and choose the fastest one.
|
- `fallback`: Connect to default network or preferred networks specified in `network_type` concurrently, and try fallback networks when unavailable or timeout.
|
||||||
- `wifi`: Prioritize WIFI, but try all other interfaces when unavailable or timeout.
|
|
||||||
- `cellular`: Prioritize Cellular, but try all other interfaces when unavailable or timeout.
|
|
||||||
- `ethernet`: Prioritize Ethernet, but try all other interfaces when unavailable or timeout.
|
|
||||||
- `wifi_only`: Connect to WIFI only.
|
|
||||||
- `cellular_only`: Connect to Cellular only.
|
|
||||||
- `ethernet_only`: Connect to Ethernet only.
|
|
||||||
|
|
||||||
For fallback strategies, when preferred interfaces fails or times out,
|
For fallback, when preferred interfaces fails or times out,
|
||||||
it will enter a 15s fast fallback state (upgraded to `hybrid`),
|
it will enter a 15s fast fallback state (Connect to all preferred and fallback networks concurrently),
|
||||||
and exit immediately if recovers.
|
and exit immediately if preferred networks recover.
|
||||||
|
|
||||||
Conflicts with `bind_interface`, `inet4_bind_address` and `inet6_bind_address`.
|
Conflicts with `bind_interface`, `inet4_bind_address` and `inet6_bind_address`.
|
||||||
|
|
||||||
|
#### network_type
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
Only supported in graphical clients on Android and Apple platforms with `auto_detect_interface` enabled.
|
||||||
|
|
||||||
|
Network types to use when using `default` or `hybrid` network strategy or
|
||||||
|
preferred network types to use when using `fallback` network strategy.
|
||||||
|
|
||||||
|
Available values: `wifi`, `cellular`, `ethernet`, `other`.
|
||||||
|
|
||||||
|
Device's default network is used by default.
|
||||||
|
|
||||||
|
#### fallback_network_type
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
Only supported in graphical clients on Android and Apple platforms with `auto_detect_interface` enabled.
|
||||||
|
|
||||||
|
Fallback network types when preferred networks are unavailable or timeout when using `fallback` network strategy.
|
||||||
|
|
||||||
|
All other networks expect preferred are used by default.
|
||||||
|
|
||||||
#### fallback_delay
|
#### fallback_delay
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.11.0"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
Only supported in graphical clients on Android and Apple platforms with `auto_detect_interface` enabled.
|
||||||
|
|
||||||
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
|
The length of time to wait before spawning a RFC 6555 Fast Fallback connection.
|
||||||
|
|
||||||
For `domain_strategy`, is the amount of time to wait for connection to succeed before assuming
|
For `domain_strategy`, is the amount of time to wait for connection to succeed before assuming
|
||||||
|
|
|
@ -6,6 +6,8 @@ icon: material/new-box
|
||||||
|
|
||||||
:material-plus: [network_strategy](#network_strategy)
|
:material-plus: [network_strategy](#network_strategy)
|
||||||
:material-alert: [fallback_delay](#fallback_delay)
|
:material-alert: [fallback_delay](#fallback_delay)
|
||||||
|
:material-alert: [network_type](#network_type)
|
||||||
|
:material-alert: [fallback_network_type](#fallback_network_type)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
|
@ -23,10 +25,16 @@ icon: material/new-box
|
||||||
"udp_fragment": false,
|
"udp_fragment": false,
|
||||||
"domain_strategy": "prefer_ipv6",
|
"domain_strategy": "prefer_ipv6",
|
||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
|
"network_type": [],
|
||||||
|
"fallback_network_type": [],
|
||||||
"fallback_delay": "300ms"
|
"fallback_delay": "300ms"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
|
||||||
#### detour
|
#### detour
|
||||||
|
@ -99,26 +107,48 @@ icon: material/new-box
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
仅在 Android 与 iOS 平台图形客户端中支持。
|
仅在 Android 与 iOS 平台图形客户端中支持,并且需要 `route.auto_detect_interface`。
|
||||||
|
|
||||||
用于选择网络接口的策略。
|
用于选择网络接口的策略。
|
||||||
|
|
||||||
可用值:
|
可用值:
|
||||||
|
|
||||||
- `default` (默认): 连接到默认接口,
|
- `default`(默认值):按顺序连接默认网络或 `network_type` 中指定的网络。
|
||||||
- `fallback`: 如果超时,尝试所有剩余接口。
|
- `hybrid`:同时连接所有网络或 `network_type` 中指定的网络。
|
||||||
- `hybrid`: 同时尝试所有接口,选择最快的一个。
|
- `fallback`:同时连接默认网络或 `network_type` 中指定的首选网络,当不可用或超时时尝试回退网络。
|
||||||
- `wifi`: 优先使用 WIFI,但在不可用或超时时尝试所有其他接口。
|
|
||||||
- `cellular`: 优先使用蜂窝数据,但在不可用或超时时尝试所有其他接口。
|
|
||||||
- `ethernet`: 优先使用以太网,但在不可用或超时时尝试所有其他接口。
|
|
||||||
- `wifi_only`: 仅连接到 WIFI。
|
|
||||||
- `cellular_only`: 仅连接到蜂窝数据。
|
|
||||||
- `ethernet_only`: 仅连接到以太网。
|
|
||||||
|
|
||||||
对于回退策略, 当优先使用的接口发生故障或超时时, 将进入 15 秒的快速回退状态(升级为 `hybrid`), 且恢复后立即退出。
|
对于回退模式,当首选接口失败或超时时,
|
||||||
|
将进入15秒的快速回退状态(同时连接所有首选和回退网络),
|
||||||
|
如果首选网络恢复,则立即退出。
|
||||||
|
|
||||||
与 `bind_interface`, `bind_inet4_address` 和 `bind_inet6_address` 冲突。
|
与 `bind_interface`, `bind_inet4_address` 和 `bind_inet6_address` 冲突。
|
||||||
|
|
||||||
|
#### network_type
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
仅在 Android 与 iOS 平台图形客户端中支持,并且需要 `route.auto_detect_interface`。
|
||||||
|
|
||||||
|
当使用 `default` 或 `hybrid` 网络策略时要使用的网络类型,或当使用 `fallback` 网络策略时要使用的首选网络类型。
|
||||||
|
|
||||||
|
可用值:`wifi`, `cellular`, `ethernet`, `other`。
|
||||||
|
|
||||||
|
默认使用设备默认网络。
|
||||||
|
|
||||||
|
#### fallback_network_type
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.11.0 起"
|
||||||
|
|
||||||
|
!!! quote ""
|
||||||
|
|
||||||
|
仅在 Android 与 iOS 平台图形客户端中支持,并且需要 `route.auto_detect_interface`。
|
||||||
|
|
||||||
|
当使用 `fallback` 网络策略时,在首选网络不可用或超时的情况下要使用的回退网络类型。
|
||||||
|
|
||||||
|
默认使用除首选网络外的所有其他网络。
|
||||||
|
|
||||||
#### fallback_delay
|
#### fallback_delay
|
||||||
|
|
||||||
在生成 RFC 6555 快速回退连接之前等待的时间长度。
|
在生成 RFC 6555 快速回退连接之前等待的时间长度。
|
||||||
|
|
|
@ -34,10 +34,10 @@ type InterfaceUpdateListener interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
InterfaceTypeWIFI = C.InterfaceTypeWIFI
|
InterfaceTypeWIFI = int32(C.InterfaceTypeWIFI)
|
||||||
InterfaceTypeCellular = C.InterfaceTypeCellular
|
InterfaceTypeCellular = int32(C.InterfaceTypeCellular)
|
||||||
InterfaceTypeEthernet = C.InterfaceTypeEthernet
|
InterfaceTypeEthernet = int32(C.InterfaceTypeEthernet)
|
||||||
InterfaceTypeOther = C.InterfaceTypeOther
|
InterfaceTypeOther = int32(C.InterfaceTypeOther)
|
||||||
)
|
)
|
||||||
|
|
||||||
type NetworkInterface struct {
|
type NetworkInterface struct {
|
||||||
|
@ -47,7 +47,7 @@ type NetworkInterface struct {
|
||||||
Addresses StringIterator
|
Addresses StringIterator
|
||||||
Flags int32
|
Flags int32
|
||||||
|
|
||||||
Type string
|
Type int32
|
||||||
DNSServer StringIterator
|
DNSServer StringIterator
|
||||||
Metered bool
|
Metered bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ func (w *platformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, err
|
||||||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||||
Flags: linkFlags(uint32(netInterface.Flags)),
|
Flags: linkFlags(uint32(netInterface.Flags)),
|
||||||
},
|
},
|
||||||
Type: netInterface.Type,
|
Type: C.InterfaceType(netInterface.Type),
|
||||||
DNSServers: iteratorToArray[string](netInterface.DNSServer),
|
DNSServers: iteratorToArray[string](netInterface.DNSServer),
|
||||||
Expensive: netInterface.Metered || isDefault && w.isExpensive,
|
Expensive: netInterface.Metered || isDefault && w.isExpensive,
|
||||||
Constrained: isDefault && w.isConstrained,
|
Constrained: isDefault && w.isConstrained,
|
||||||
|
|
|
@ -70,7 +70,7 @@ type DialerOptions struct {
|
||||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||||
ProtectPath string `json:"protect_path,omitempty"`
|
ProtectPath string `json:"protect_path,omitempty"`
|
||||||
RoutingMark uint32 `json:"routing_mark,omitempty"`
|
RoutingMark FwMark `json:"routing_mark,omitempty"`
|
||||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||||
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
||||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||||
|
@ -79,6 +79,8 @@ type DialerOptions struct {
|
||||||
UDPFragmentDefault bool `json:"-"`
|
UDPFragmentDefault bool `json:"-"`
|
||||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||||
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
||||||
|
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||||
|
FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"`
|
||||||
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
||||||
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
||||||
IsWireGuardListener bool `json:"-"`
|
IsWireGuardListener bool `json:"-"`
|
||||||
|
|
|
@ -12,8 +12,10 @@ type RouteOptions struct {
|
||||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||||
DefaultInterface string `json:"default_interface,omitempty"`
|
DefaultInterface string `json:"default_interface,omitempty"`
|
||||||
DefaultMark uint32 `json:"default_mark,omitempty"`
|
DefaultMark FwMark `json:"default_mark,omitempty"`
|
||||||
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
||||||
|
DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"`
|
||||||
|
DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"`
|
||||||
DefaultFallbackDelay badoption.Duration `json:"default_fallback_delay,omitempty"`
|
DefaultFallbackDelay badoption.Duration `json:"default_fallback_delay,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ type RawDefaultRule struct {
|
||||||
User badoption.Listable[string] `json:"user,omitempty"`
|
User badoption.Listable[string] `json:"user,omitempty"`
|
||||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||||
ClashMode string `json:"clash_mode,omitempty"`
|
ClashMode string `json:"clash_mode,omitempty"`
|
||||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
|
|
|
@ -97,7 +97,7 @@ type RawDefaultDNSRule struct {
|
||||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||||
ClashMode string `json:"clash_mode,omitempty"`
|
ClashMode string `json:"clash_mode,omitempty"`
|
||||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
|
|
|
@ -162,7 +162,7 @@ type DefaultHeadlessRule struct {
|
||||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||||
|
|
|
@ -171,3 +171,27 @@ func (n *NetworkStrategy) UnmarshalJSON(content []byte) error {
|
||||||
*n = NetworkStrategy(strategy)
|
*n = NetworkStrategy(strategy)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InterfaceType C.InterfaceType
|
||||||
|
|
||||||
|
func (t InterfaceType) Build() C.InterfaceType {
|
||||||
|
return C.InterfaceType(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t InterfaceType) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(C.InterfaceType(t).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *InterfaceType) UnmarshalJSON(content []byte) error {
|
||||||
|
var value string
|
||||||
|
err := json.Unmarshal(content, &value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
interfaceType, loaded := C.StringToInterfaceType[value]
|
||||||
|
if !loaded {
|
||||||
|
return E.New("unknown interface type: ", value)
|
||||||
|
}
|
||||||
|
*t = InterfaceType(interfaceType)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
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"
|
||||||
dns "github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
@ -37,6 +37,8 @@ type Outbound struct {
|
||||||
domainStrategy dns.DomainStrategy
|
domainStrategy dns.DomainStrategy
|
||||||
fallbackDelay time.Duration
|
fallbackDelay time.Duration
|
||||||
networkStrategy C.NetworkStrategy
|
networkStrategy C.NetworkStrategy
|
||||||
|
networkType []C.InterfaceType
|
||||||
|
fallbackNetworkType []C.InterfaceType
|
||||||
networkFallbackDelay time.Duration
|
networkFallbackDelay time.Duration
|
||||||
overrideOption int
|
overrideOption int
|
||||||
overrideDestination M.Socksaddr
|
overrideDestination M.Socksaddr
|
||||||
|
@ -55,6 +57,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||||
networkStrategy: C.NetworkStrategy(options.NetworkStrategy),
|
networkStrategy: C.NetworkStrategy(options.NetworkStrategy),
|
||||||
|
networkType: common.Map(options.NetworkType, option.InterfaceType.Build),
|
||||||
|
fallbackNetworkType: common.Map(options.FallbackNetworkType, option.InterfaceType.Build),
|
||||||
networkFallbackDelay: time.Duration(options.NetworkFallbackDelay),
|
networkFallbackDelay: time.Duration(options.NetworkFallbackDelay),
|
||||||
dialer: outboundDialer,
|
dialer: outboundDialer,
|
||||||
// loopBack: newLoopBackDetector(router),
|
// loopBack: newLoopBackDetector(router),
|
||||||
|
@ -171,10 +175,10 @@ func (h *Outbound) DialParallel(ctx context.Context, network string, destination
|
||||||
return nil, E.New("no IPv6 address available for ", destination)
|
return nil, E.New("no IPv6 address available for ", destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.networkStrategy, h.fallbackDelay)
|
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.networkStrategy, h.networkType, h.fallbackNetworkType, h.fallbackDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error) {
|
func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
@ -210,10 +214,10 @@ func (h *Outbound) DialParallelNetwork(ctx context.Context, network string, dest
|
||||||
return nil, E.New("no IPv6 address available for ", destination)
|
return nil, E.New("no IPv6 address available for ", destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, networkStrategy, fallbackDelay)
|
return dialer.DialParallelNetwork(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.Socksaddr, destinationAddresses []netip.Addr, networkStrategy C.NetworkStrategy, networkType []C.InterfaceType, fallbackNetworkType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, netip.Addr, error) {
|
||||||
ctx, metadata := adapter.ExtendContext(ctx)
|
ctx, metadata := adapter.ExtendContext(ctx)
|
||||||
metadata.Outbound = h.Tag()
|
metadata.Outbound = h.Tag()
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
|
@ -232,7 +236,7 @@ func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.
|
||||||
} else {
|
} else {
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
}
|
}
|
||||||
conn, newDestination, err := dialer.ListenSerialNetworkPacket(ctx, h.dialer, destination, destinationAddresses, networkStrategy, fallbackDelay)
|
conn, newDestination, err := dialer.ListenSerialNetworkPacket(ctx, h.dialer, destination, destinationAddresses, networkStrategy, networkType, fallbackNetworkType, fallbackDelay)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, netip.Addr{}, err
|
return nil, netip.Addr{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,10 +59,12 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
|
||||||
interfaceFinder: control.NewDefaultInterfaceFinder(),
|
interfaceFinder: control.NewDefaultInterfaceFinder(),
|
||||||
autoDetectInterface: routeOptions.AutoDetectInterface,
|
autoDetectInterface: routeOptions.AutoDetectInterface,
|
||||||
defaultOptions: adapter.NetworkOptions{
|
defaultOptions: adapter.NetworkOptions{
|
||||||
DefaultInterface: routeOptions.DefaultInterface,
|
BindInterface: routeOptions.DefaultInterface,
|
||||||
DefaultMark: routeOptions.DefaultMark,
|
RoutingMark: uint32(routeOptions.DefaultMark),
|
||||||
DefaultNetworkStrategy: C.NetworkStrategy(routeOptions.DefaultNetworkStrategy),
|
NetworkStrategy: C.NetworkStrategy(routeOptions.DefaultNetworkStrategy),
|
||||||
DefaultFallbackDelay: time.Duration(routeOptions.DefaultFallbackDelay),
|
NetworkType: common.Map(routeOptions.DefaultNetworkType, option.InterfaceType.Build),
|
||||||
|
FallbackNetworkType: common.Map(routeOptions.DefaultFallbackNetworkType, option.InterfaceType.Build),
|
||||||
|
FallbackDelay: time.Duration(routeOptions.DefaultFallbackDelay),
|
||||||
},
|
},
|
||||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||||
|
@ -385,7 +387,7 @@ func (r *NetworkManager) notifyInterfaceUpdate(defaultInterface *control.Interfa
|
||||||
networkInterface := common.Find(r.networkInterfaces.Load(), func(it adapter.NetworkInterface) bool {
|
networkInterface := common.Find(r.networkInterfaces.Load(), func(it adapter.NetworkInterface) bool {
|
||||||
return it.Interface.Index == defaultInterface.Index
|
return it.Interface.Index == defaultInterface.Index
|
||||||
})
|
})
|
||||||
if networkInterface.Type == "" {
|
if networkInterface.Name == "" {
|
||||||
// race
|
// race
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,8 @@ func (r *RuleActionRoute) String() string {
|
||||||
|
|
||||||
type RuleActionRouteOptions struct {
|
type RuleActionRouteOptions struct {
|
||||||
NetworkStrategy C.NetworkStrategy
|
NetworkStrategy C.NetworkStrategy
|
||||||
|
NetworkType []C.InterfaceType
|
||||||
|
FallbackNetworkType []C.InterfaceType
|
||||||
FallbackDelay time.Duration
|
FallbackDelay time.Duration
|
||||||
UDPDisableDomainUnmapping bool
|
UDPDisableDomainUnmapping bool
|
||||||
UDPConnect bool
|
UDPConnect bool
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||||
"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/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
@ -224,7 +225,7 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.NetworkType) > 0 {
|
if len(options.NetworkType) > 0 {
|
||||||
item := NewNetworkTypeItem(networkManager, options.NetworkType)
|
item := NewNetworkTypeItem(networkManager, common.Map(options.NetworkType, option.InterfaceType.Build))
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
if len(options.NetworkType) > 0 {
|
if len(options.NetworkType) > 0 {
|
||||||
item := NewNetworkTypeItem(networkManager, options.NetworkType)
|
item := NewNetworkTypeItem(networkManager, common.Map(options.NetworkType, option.InterfaceType.Build))
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
@ -142,7 +143,7 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
||||||
}
|
}
|
||||||
if networkManager != nil {
|
if networkManager != nil {
|
||||||
if len(options.NetworkType) > 0 {
|
if len(options.NetworkType) > 0 {
|
||||||
item := NewNetworkTypeItem(networkManager, options.NetworkType)
|
item := NewNetworkTypeItem(networkManager, common.Map(options.NetworkType, option.InterfaceType.Build))
|
||||||
rule.items = append(rule.items, item)
|
rule.items = append(rule.items, item)
|
||||||
rule.allItems = append(rule.allItems, item)
|
rule.allItems = append(rule.allItems, item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
)
|
)
|
||||||
|
@ -12,10 +13,10 @@ var _ RuleItem = (*NetworkTypeItem)(nil)
|
||||||
|
|
||||||
type NetworkTypeItem struct {
|
type NetworkTypeItem struct {
|
||||||
networkManager adapter.NetworkManager
|
networkManager adapter.NetworkManager
|
||||||
networkType []string
|
networkType []C.InterfaceType
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNetworkTypeItem(networkManager adapter.NetworkManager, networkType []string) *NetworkTypeItem {
|
func NewNetworkTypeItem(networkManager adapter.NetworkManager, networkType []C.InterfaceType) *NetworkTypeItem {
|
||||||
return &NetworkTypeItem{
|
return &NetworkTypeItem{
|
||||||
networkManager: networkManager,
|
networkManager: networkManager,
|
||||||
networkType: networkType,
|
networkType: networkType,
|
||||||
|
|
Loading…
Reference in a new issue