mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-28 11:31:29 +00:00
Refactor multi networks strategy
This commit is contained in:
parent
0ce2259562
commit
fb37d7c4dd
|
@ -69,6 +69,8 @@ type InboundContext struct {
|
|||
UDPDisableDomainUnmapping bool
|
||||
UDPConnect bool
|
||||
NetworkStrategy C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
|
||||
DNSServer string
|
||||
|
|
|
@ -28,10 +28,12 @@ type NetworkManager interface {
|
|||
}
|
||||
|
||||
type NetworkOptions struct {
|
||||
DefaultNetworkStrategy C.NetworkStrategy
|
||||
DefaultFallbackDelay time.Duration
|
||||
DefaultInterface string
|
||||
DefaultMark uint32
|
||||
NetworkStrategy C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
BindInterface string
|
||||
RoutingMark uint32
|
||||
}
|
||||
|
||||
type InterfaceUpdateListener interface {
|
||||
|
@ -45,7 +47,7 @@ type WIFIState struct {
|
|||
|
||||
type NetworkInterface struct {
|
||||
control.Interface
|
||||
Type string
|
||||
Type C.InterfaceType
|
||||
DNSServers []string
|
||||
Expensive bool
|
||||
Constrained bool
|
||||
|
|
|
@ -26,7 +26,7 @@ func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata a
|
|||
var err error
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
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 {
|
||||
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 len(metadata.DestinationAddresses) > 0 {
|
||||
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 {
|
||||
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 {
|
||||
if len(metadata.DestinationAddresses) > 0 {
|
||||
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 {
|
||||
outPacketConn, destinationAddress, err = N.ListenSerial(ctx, this, metadata.Destination, metadata.DestinationAddresses)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/sagernet/sing-box/common/conntrack"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/atomic"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
@ -33,6 +34,8 @@ type DefaultDialer struct {
|
|||
isWireGuardListener bool
|
||||
networkManager adapter.NetworkManager
|
||||
networkStrategy C.NetworkStrategy
|
||||
networkType []C.InterfaceType
|
||||
fallbackNetworkType []C.InterfaceType
|
||||
networkFallbackDelay time.Duration
|
||||
networkLastFallback atomic.TypedValue[time.Time]
|
||||
}
|
||||
|
@ -43,6 +46,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
|||
listener net.ListenConfig
|
||||
interfaceFinder control.InterfaceFinder
|
||||
networkStrategy C.NetworkStrategy
|
||||
networkType []C.InterfaceType
|
||||
fallbackNetworkType []C.InterfaceType
|
||||
networkFallbackDelay time.Duration
|
||||
)
|
||||
if networkManager != nil {
|
||||
|
@ -56,8 +61,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
|||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
}
|
||||
if options.RoutingMark > 0 {
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(options.RoutingMark))
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(options.RoutingMark))
|
||||
dialer.Control = control.Append(dialer.Control, control.RoutingMark(uint32(options.RoutingMark)))
|
||||
listener.Control = control.Append(listener.Control, control.RoutingMark(uint32(options.RoutingMark)))
|
||||
}
|
||||
if networkManager != nil {
|
||||
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`")
|
||||
}
|
||||
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)
|
||||
if networkManager == nil || !networkManager.AutoDetectInterface() {
|
||||
return nil, E.New("`route.auto_detect_interface` is require by `network_strategy`")
|
||||
|
@ -81,14 +88,16 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
|||
}
|
||||
if networkManager != nil && options.BindInterface == "" && options.Inet4BindAddress == nil && options.Inet6BindAddress == nil {
|
||||
defaultOptions := networkManager.DefaultOptions()
|
||||
if defaultOptions.DefaultInterface != "" {
|
||||
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)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
} else if networkManager.AutoDetectInterface() {
|
||||
if defaultOptions.DefaultNetworkStrategy != C.NetworkStrategyDefault && C.NetworkStrategy(options.NetworkStrategy) == C.NetworkStrategyDefault {
|
||||
networkStrategy = defaultOptions.DefaultNetworkStrategy
|
||||
networkFallbackDelay = defaultOptions.DefaultFallbackDelay
|
||||
if defaultOptions.NetworkStrategy != C.NetworkStrategyDefault && C.NetworkStrategy(options.NetworkStrategy) == C.NetworkStrategyDefault {
|
||||
networkStrategy = defaultOptions.NetworkStrategy
|
||||
networkType = defaultOptions.NetworkType
|
||||
fallbackNetworkType = defaultOptions.FallbackNetworkType
|
||||
networkFallbackDelay = defaultOptions.FallbackDelay
|
||||
bindFunc := networkManager.ProtectFunc()
|
||||
dialer.Control = control.Append(dialer.Control, bindFunc)
|
||||
listener.Control = control.Append(listener.Control, bindFunc)
|
||||
|
@ -179,6 +188,8 @@ func NewDefault(networkManager adapter.NetworkManager, options option.DialerOpti
|
|||
isWireGuardListener: options.IsWireGuardListener,
|
||||
networkManager: networkManager,
|
||||
networkStrategy: networkStrategy,
|
||||
networkType: networkType,
|
||||
fallbackNetworkType: fallbackNetworkType,
|
||||
networkFallbackDelay: networkFallbackDelay,
|
||||
}, nil
|
||||
}
|
||||
|
@ -202,11 +213,11 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address
|
|||
return trackConn(DialSlowContext(&d.dialer6, ctx, network, address))
|
||||
}
|
||||
} 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 {
|
||||
return d.DialContext(ctx, network, address)
|
||||
}
|
||||
|
@ -226,9 +237,9 @@ func (d *DefaultDialer) DialParallelInterface(ctx context.Context, network strin
|
|||
err error
|
||||
)
|
||||
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 {
|
||||
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 {
|
||||
return nil, err
|
||||
|
@ -249,11 +260,11 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
|
|||
return trackPacketConn(d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4))
|
||||
}
|
||||
} 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 {
|
||||
return d.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
@ -264,11 +275,11 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
|||
if destination.IsIPv4() && !destination.Addr.IsUnspecified() {
|
||||
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) {
|
||||
return trackPacketConn(d.listenSerialInterfacePacket(context.Background(), d.udpListener, network, address, d.networkStrategy, d.networkFallbackDelay))
|
||||
return trackPacketConn(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) {
|
||||
|
|
|
@ -7,14 +7,14 @@ import (
|
|||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
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) {
|
||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy)
|
||||
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, interfaceType, fallbackInterfaceType)
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
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) {
|
||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy)
|
||||
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, interfaceType, fallbackInterfaceType)
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
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) {
|
||||
primaryInterfaces, fallbackInterfaces := selectInterfaces(d.networkManager, strategy)
|
||||
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, interfaceType, fallbackInterfaceType)
|
||||
if len(primaryInterfaces)+len(fallbackInterfaces) == 0 {
|
||||
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...)
|
||||
}
|
||||
|
||||
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()
|
||||
switch strategy {
|
||||
case C.NetworkStrategyFallback:
|
||||
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||
if defaultIf != nil {
|
||||
case C.NetworkStrategyDefault:
|
||||
if len(interfaceType) == 0 {
|
||||
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||
for _, iif := range interfaces {
|
||||
if iif.Index == defaultIf.Index {
|
||||
primaryInterfaces = append(primaryInterfaces, iif)
|
||||
|
@ -188,54 +188,36 @@ func selectInterfaces(networkManager adapter.NetworkManager, strategy C.NetworkS
|
|||
}
|
||||
}
|
||||
} else {
|
||||
primaryInterfaces = interfaces
|
||||
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
case C.NetworkStrategyHybrid:
|
||||
primaryInterfaces = interfaces
|
||||
case C.NetworkStrategyWIFI:
|
||||
for _, iif := range interfaces {
|
||||
if iif.Type == C.InterfaceTypeWIFI {
|
||||
primaryInterfaces = append(primaryInterfaces, iif)
|
||||
} else {
|
||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
||||
}
|
||||
if len(interfaceType) == 0 {
|
||||
primaryInterfaces = interfaces
|
||||
} else {
|
||||
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
case C.NetworkStrategyCellular:
|
||||
for _, iif := range interfaces {
|
||||
if iif.Type == C.InterfaceTypeCellular {
|
||||
primaryInterfaces = append(primaryInterfaces, iif)
|
||||
} else {
|
||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
||||
case C.NetworkStrategyFallback:
|
||||
if len(interfaceType) == 0 {
|
||||
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||
for _, iif := range interfaces {
|
||||
if iif.Index == defaultIf.Index {
|
||||
primaryInterfaces = append(primaryInterfaces, iif)
|
||||
} else {
|
||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(interfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
case C.NetworkStrategyEthernet:
|
||||
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))
|
||||
fallbackInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||
return common.Contains(fallbackInterfaceType, iif.Type)
|
||||
})
|
||||
}
|
||||
return primaryInterfaces, fallbackInterfaces
|
||||
}
|
||||
|
|
|
@ -13,13 +13,13 @@ import (
|
|||
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 {
|
||||
return parallelDialer.DialParallelNetwork(ctx, network, destination, destinationAddresses, strategy, fallbackDelay)
|
||||
return parallelDialer.DialParallelNetwork(ctx, network, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
}
|
||||
var errors []error
|
||||
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 {
|
||||
return conn, nil
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func DialSerialNetwork(ctx context.Context, dialer ParallelInterfaceDialer, netw
|
|||
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 {
|
||||
fallbackDelay = N.DefaultFallbackDelay
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, ne
|
|||
return address.Is6() && !address.Is4In6()
|
||||
})
|
||||
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
|
||||
if preferIPv6 {
|
||||
|
@ -65,7 +65,7 @@ func DialParallelNetwork(ctx context.Context, dialer ParallelInterfaceDialer, ne
|
|||
if !primary {
|
||||
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 {
|
||||
case results <- dialResult{Conn: c, error: err, primary: primary, done: true}:
|
||||
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 {
|
||||
return parallelDialer.ListenSerialNetworkPacket(ctx, destination, destinationAddresses, strategy, fallbackDelay)
|
||||
return parallelDialer.ListenSerialNetworkPacket(ctx, destination, destinationAddresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay)
|
||||
}
|
||||
var errors []error
|
||||
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 {
|
||||
return conn, address, nil
|
||||
}
|
||||
|
|
|
@ -77,11 +77,11 @@ func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInter
|
|||
|
||||
type ParallelInterfaceDialer interface {
|
||||
N.Dialer
|
||||
DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.Conn, error)
|
||||
ListenSerialInterfacePacket(ctx context.Context, destination M.Socksaddr, strategy C.NetworkStrategy, fallbackDelay time.Duration) (net.PacketConn, 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, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.PacketConn, error)
|
||||
}
|
||||
|
||||
type ParallelNetworkDialer interface {
|
||||
DialParallelNetwork(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr, strategy C.NetworkStrategy, 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)
|
||||
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, 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
|
||||
}
|
||||
|
||||
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() {
|
||||
return d.dialer.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
@ -128,13 +128,13 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context
|
|||
fallbackDelay = d.fallbackDelay
|
||||
}
|
||||
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 {
|
||||
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() {
|
||||
return d.dialer.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C
|
|||
if err != nil {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -226,7 +226,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
|||
}
|
||||
rule.AdGuardDomainMatcher = matcher
|
||||
case ruleItemNetworkType:
|
||||
rule.NetworkType, err = readRuleItemString(reader)
|
||||
rule.NetworkType, err = readRuleItemUint8[option.InterfaceType](reader)
|
||||
case ruleItemNetworkIsExpensive:
|
||||
rule.NetworkIsExpensive = true
|
||||
case ruleItemNetworkIsConstrained:
|
||||
|
@ -349,7 +349,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
|||
if generateVersion < C.RuleSetVersion3 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -414,6 +414,18 @@ func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) e
|
|||
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) {
|
||||
return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
|
||||
}
|
||||
|
|
|
@ -5,44 +5,52 @@ import (
|
|||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
type InterfaceType uint8
|
||||
|
||||
const (
|
||||
InterfaceTypeWIFI = "wifi"
|
||||
InterfaceTypeCellular = "cellular"
|
||||
InterfaceTypeEthernet = "ethernet"
|
||||
InterfaceTypeOther = "other"
|
||||
InterfaceTypeWIFI InterfaceType = iota
|
||||
InterfaceTypeCellular
|
||||
InterfaceTypeEthernet
|
||||
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 (
|
||||
NetworkStrategyDefault NetworkStrategy = iota
|
||||
NetworkStrategyFallback
|
||||
NetworkStrategyHybrid
|
||||
NetworkStrategyWIFI
|
||||
NetworkStrategyCellular
|
||||
NetworkStrategyEthernet
|
||||
NetworkStrategyWIFIOnly
|
||||
NetworkStrategyCellularOnly
|
||||
NetworkStrategyEthernetOnly
|
||||
)
|
||||
|
||||
var (
|
||||
NetworkStrategyToString = map[NetworkStrategy]string{
|
||||
NetworkStrategyDefault: "default",
|
||||
NetworkStrategyFallback: "fallback",
|
||||
NetworkStrategyHybrid: "hybrid",
|
||||
NetworkStrategyWIFI: "wifi",
|
||||
NetworkStrategyCellular: "cellular",
|
||||
NetworkStrategyEthernet: "ethernet",
|
||||
NetworkStrategyWIFIOnly: "wifi_only",
|
||||
NetworkStrategyCellularOnly: "cellular_only",
|
||||
NetworkStrategyEthernetOnly: "ethernet_only",
|
||||
networkStrategyToString = map[NetworkStrategy]string{
|
||||
NetworkStrategyDefault: "default",
|
||||
NetworkStrategyFallback: "fallback",
|
||||
NetworkStrategyHybrid: "hybrid",
|
||||
}
|
||||
StringToNetworkStrategy = common.ReverseMap(NetworkStrategyToString)
|
||||
StringToNetworkStrategy = common.ReverseMap(networkStrategyToString)
|
||||
)
|
||||
|
||||
func (s NetworkStrategy) String() string {
|
||||
name, loaded := NetworkStrategyToString[s]
|
||||
name, loaded := networkStrategyToString[s]
|
||||
if !loaded {
|
||||
return F.ToString(int(s))
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ icon: material/new-box
|
|||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
: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)
|
||||
|
||||
!!! quote "Changes in sing-box 1.8.0"
|
||||
|
@ -30,17 +32,18 @@ icon: material/new-box
|
|||
"default_interface": "",
|
||||
"default_mark": 0,
|
||||
"default_network_strategy": "",
|
||||
"default_network_type": [],
|
||||
"default_fallback_network_type": [],
|
||||
"default_fallback_delay": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fields
|
||||
!!! note ""
|
||||
|
||||
| Key | Format |
|
||||
|-----------|-----------------------|
|
||||
| `geoip` | [GeoIP](./geoip/) |
|
||||
| `geosite` | [Geosite](./geosite/) |
|
||||
You can ignore the JSON Array [] tag when the content is only one item
|
||||
|
||||
### Fields
|
||||
|
||||
#### rules
|
||||
|
||||
|
@ -96,11 +99,9 @@ Takes no effect if `outbound.routing_mark` is set.
|
|||
|
||||
#### 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.
|
||||
|
||||
Strategy for selecting network interfaces.
|
||||
See [Dial Fields](/configuration/shared/dial/#network_strategy) for details.
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
|
||||
!!! 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 中的更改"
|
||||
|
||||
:material-plus: [rule_set](#rule_set)
|
||||
|
@ -26,6 +37,10 @@
|
|||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||
|
||||
### 字段
|
||||
|
||||
| 键 | 格式 |
|
||||
|
@ -87,11 +102,9 @@
|
|||
|
||||
#### 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` 已设置时不生效。
|
||||
|
||||
|
@ -99,12 +112,20 @@
|
|||
|
||||
与 `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
|
||||
"outbound": "",
|
||||
"network_strategy": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "",
|
||||
"udp_disable_domain_unmapping": 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.
|
||||
|
||||
#### outbound
|
||||
|
@ -27,23 +33,21 @@ Tag of target outbound.
|
|||
|
||||
#### network_strategy
|
||||
|
||||
!!! quote ""
|
||||
|
||||
Only supported in graphical clients on Android and iOS with `auto_detect_interface` enabled.
|
||||
|
||||
Strategy for selecting network interfaces.
|
||||
See [Dial Fields](/configuration/shared/dial/#network_strategy) for details.
|
||||
|
||||
Only take effect if outbound is direct without `outbound.bind_interface`,
|
||||
`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
|
||||
|
||||
!!! 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.
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
|
|
@ -12,6 +12,8 @@ icon: material/new-box
|
|||
"outbound": "",
|
||||
"network_strategy": "",
|
||||
"fallback_delay": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"udp_disable_domain_unmapping": false,
|
||||
"udp_connect": false
|
||||
}
|
||||
|
@ -27,23 +29,21 @@ icon: material/new-box
|
|||
|
||||
#### network_strategy
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持,并且需要 `auto_detect_interface`。
|
||||
|
||||
选择网络接口的策略。
|
||||
详情参阅 [拨号字段](/configuration/shared/dial/#network_strategy)。
|
||||
|
||||
仅当出站为 `direct` 且 `outbound.bind_interface`, `outbound.inet4_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
|
||||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 Apple 平台图形客户端中支持,并且需要 `auto_detect_interface` 且 `network_strategy` 已设置。
|
||||
|
||||
详情参阅 [拨号字段](/configuration/shared/dial/#fallback_delay)。
|
||||
|
||||
#### udp_disable_domain_unmapping
|
||||
|
@ -68,6 +68,10 @@ icon: material/new-box
|
|||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||
|
||||
`route-options` 为路由设置选项。
|
||||
|
||||
### reject
|
||||
|
|
|
@ -5,7 +5,9 @@ icon: material/new-box
|
|||
!!! quote "Changes in sing-box 1.11.0"
|
||||
|
||||
: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
|
||||
|
||||
|
@ -23,10 +25,16 @@ icon: material/new-box
|
|||
"udp_fragment": false,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"network_strategy": "default",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "300ms"
|
||||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
You can ignore the JSON Array [] tag when the content is only one item
|
||||
|
||||
### Fields
|
||||
|
||||
#### detour
|
||||
|
@ -101,30 +109,57 @@ If set, the requested domain name will be resolved to IP before connect.
|
|||
|
||||
!!! 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.
|
||||
|
||||
Available values:
|
||||
|
||||
- `default` (default): Connect to the default interface.
|
||||
- `fallback`: Try all other interfaces when timeout.
|
||||
- `hybrid`: Connect to all interfaces concurrently and choose the fastest one.
|
||||
- `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.
|
||||
- `default` (default): Connect to default network or networks specified in `network_type` sequentially.
|
||||
- `hybrid`: Connect to all networks or networks specified in `network_type` concurrently.
|
||||
- `fallback`: Connect to default network or preferred networks specified in `network_type` concurrently, and try fallback networks when unavailable or timeout.
|
||||
|
||||
For fallback strategies, when preferred interfaces fails or times out,
|
||||
it will enter a 15s fast fallback state (upgraded to `hybrid`),
|
||||
and exit immediately if recovers.
|
||||
For fallback, when preferred interfaces fails or times out,
|
||||
it will enter a 15s fast fallback state (Connect to all preferred and fallback networks concurrently),
|
||||
and exit immediately if preferred networks recover.
|
||||
|
||||
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
|
||||
|
||||
!!! 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.
|
||||
|
||||
For `domain_strategy`, is the amount of time to wait for connection to succeed before assuming
|
||||
|
|
|
@ -5,7 +5,9 @@ icon: material/new-box
|
|||
!!! quote "sing-box 1.11.0 中的更改"
|
||||
|
||||
: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,
|
||||
"domain_strategy": "prefer_ipv6",
|
||||
"network_strategy": "",
|
||||
"network_type": [],
|
||||
"fallback_network_type": [],
|
||||
"fallback_delay": "300ms"
|
||||
}
|
||||
```
|
||||
|
||||
!!! note ""
|
||||
|
||||
当内容只有一项时,可以忽略 JSON 数组 [] 标签
|
||||
|
||||
### 字段
|
||||
|
||||
#### detour
|
||||
|
@ -99,26 +107,48 @@ icon: material/new-box
|
|||
|
||||
!!! quote ""
|
||||
|
||||
仅在 Android 与 iOS 平台图形客户端中支持。
|
||||
仅在 Android 与 iOS 平台图形客户端中支持,并且需要 `route.auto_detect_interface`。
|
||||
|
||||
用于选择网络接口的策略。
|
||||
|
||||
可用值:
|
||||
|
||||
- `default` (默认): 连接到默认接口,
|
||||
- `fallback`: 如果超时,尝试所有剩余接口。
|
||||
- `hybrid`: 同时尝试所有接口,选择最快的一个。
|
||||
- `wifi`: 优先使用 WIFI,但在不可用或超时时尝试所有其他接口。
|
||||
- `cellular`: 优先使用蜂窝数据,但在不可用或超时时尝试所有其他接口。
|
||||
- `ethernet`: 优先使用以太网,但在不可用或超时时尝试所有其他接口。
|
||||
- `wifi_only`: 仅连接到 WIFI。
|
||||
- `cellular_only`: 仅连接到蜂窝数据。
|
||||
- `ethernet_only`: 仅连接到以太网。
|
||||
- `default`(默认值):按顺序连接默认网络或 `network_type` 中指定的网络。
|
||||
- `hybrid`:同时连接所有网络或 `network_type` 中指定的网络。
|
||||
- `fallback`:同时连接默认网络或 `network_type` 中指定的首选网络,当不可用或超时时尝试回退网络。
|
||||
|
||||
对于回退策略, 当优先使用的接口发生故障或超时时, 将进入 15 秒的快速回退状态(升级为 `hybrid`), 且恢复后立即退出。
|
||||
对于回退模式,当首选接口失败或超时时,
|
||||
将进入15秒的快速回退状态(同时连接所有首选和回退网络),
|
||||
如果首选网络恢复,则立即退出。
|
||||
|
||||
与 `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
|
||||
|
||||
在生成 RFC 6555 快速回退连接之前等待的时间长度。
|
||||
|
|
|
@ -34,10 +34,10 @@ type InterfaceUpdateListener interface {
|
|||
}
|
||||
|
||||
const (
|
||||
InterfaceTypeWIFI = C.InterfaceTypeWIFI
|
||||
InterfaceTypeCellular = C.InterfaceTypeCellular
|
||||
InterfaceTypeEthernet = C.InterfaceTypeEthernet
|
||||
InterfaceTypeOther = C.InterfaceTypeOther
|
||||
InterfaceTypeWIFI = int32(C.InterfaceTypeWIFI)
|
||||
InterfaceTypeCellular = int32(C.InterfaceTypeCellular)
|
||||
InterfaceTypeEthernet = int32(C.InterfaceTypeEthernet)
|
||||
InterfaceTypeOther = int32(C.InterfaceTypeOther)
|
||||
)
|
||||
|
||||
type NetworkInterface struct {
|
||||
|
@ -47,7 +47,7 @@ type NetworkInterface struct {
|
|||
Addresses StringIterator
|
||||
Flags int32
|
||||
|
||||
Type string
|
||||
Type int32
|
||||
DNSServer StringIterator
|
||||
Metered bool
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ func (w *platformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, err
|
|||
Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
|
||||
Flags: linkFlags(uint32(netInterface.Flags)),
|
||||
},
|
||||
Type: netInterface.Type,
|
||||
Type: C.InterfaceType(netInterface.Type),
|
||||
DNSServers: iteratorToArray[string](netInterface.DNSServer),
|
||||
Expensive: netInterface.Metered || isDefault && w.isExpensive,
|
||||
Constrained: isDefault && w.isConstrained,
|
||||
|
|
|
@ -65,23 +65,25 @@ type DialerOptionsWrapper interface {
|
|||
}
|
||||
|
||||
type DialerOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark uint32 `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
|
||||
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
||||
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
||||
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
||||
IsWireGuardListener bool `json:"-"`
|
||||
Detour string `json:"detour,omitempty"`
|
||||
BindInterface string `json:"bind_interface,omitempty"`
|
||||
Inet4BindAddress *badoption.Addr `json:"inet4_bind_address,omitempty"`
|
||||
Inet6BindAddress *badoption.Addr `json:"inet6_bind_address,omitempty"`
|
||||
ProtectPath string `json:"protect_path,omitempty"`
|
||||
RoutingMark FwMark `json:"routing_mark,omitempty"`
|
||||
ReuseAddr bool `json:"reuse_addr,omitempty"`
|
||||
ConnectTimeout badoption.Duration `json:"connect_timeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||
UDPFragmentDefault bool `json:"-"`
|
||||
DomainStrategy DomainStrategy `json:"domain_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"`
|
||||
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
||||
IsWireGuardListener bool `json:"-"`
|
||||
}
|
||||
|
||||
func (o *DialerOptions) TakeDialerOptions() DialerOptions {
|
||||
|
|
|
@ -3,18 +3,20 @@ package option
|
|||
import "github.com/sagernet/sing/common/json/badoption"
|
||||
|
||||
type RouteOptions struct {
|
||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
RuleSet []RuleSet `json:"rule_set,omitempty"`
|
||||
Final string `json:"final,omitempty"`
|
||||
FindProcess bool `json:"find_process,omitempty"`
|
||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||
DefaultInterface string `json:"default_interface,omitempty"`
|
||||
DefaultMark uint32 `json:"default_mark,omitempty"`
|
||||
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
||||
DefaultFallbackDelay badoption.Duration `json:"default_fallback_delay,omitempty"`
|
||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
RuleSet []RuleSet `json:"rule_set,omitempty"`
|
||||
Final string `json:"final,omitempty"`
|
||||
FindProcess bool `json:"find_process,omitempty"`
|
||||
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
|
||||
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
|
||||
DefaultInterface string `json:"default_interface,omitempty"`
|
||||
DefaultMark uint32 `json:"default_mark,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"`
|
||||
}
|
||||
|
||||
type GeoIPOptions struct {
|
||||
|
|
|
@ -67,42 +67,42 @@ func (r Rule) IsValid() bool {
|
|||
}
|
||||
|
||||
type RawDefaultRule struct {
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Client badoption.Listable[string] `json:"client,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Client badoption.Listable[string] `json:"client,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
|
|
|
@ -68,44 +68,44 @@ func (r DNSRule) IsValid() bool {
|
|||
}
|
||||
|
||||
type RawDefaultDNSRule struct {
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||
IPVersion int `json:"ip_version,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
User badoption.Listable[string] `json:"user,omitempty"`
|
||||
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||
|
|
|
@ -146,28 +146,28 @@ func (r HeadlessRule) IsValid() bool {
|
|||
}
|
||||
|
||||
type DefaultHeadlessRule struct {
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
|
||||
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
|
||||
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
|
||||
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
DomainMatcher *domain.Matcher `json:"-"`
|
||||
SourceIPSet *netipx.IPSet `json:"-"`
|
||||
|
|
|
@ -171,3 +171,27 @@ func (n *NetworkStrategy) UnmarshalJSON(content []byte) error {
|
|||
*n = NetworkStrategy(strategy)
|
||||
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"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"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/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
@ -37,6 +37,8 @@ type Outbound struct {
|
|||
domainStrategy dns.DomainStrategy
|
||||
fallbackDelay time.Duration
|
||||
networkStrategy C.NetworkStrategy
|
||||
networkType []C.InterfaceType
|
||||
fallbackNetworkType []C.InterfaceType
|
||||
networkFallbackDelay time.Duration
|
||||
overrideOption int
|
||||
overrideDestination M.Socksaddr
|
||||
|
@ -55,6 +57,8 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||
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),
|
||||
dialer: outboundDialer,
|
||||
// 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 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)
|
||||
metadata.Outbound = h.Tag()
|
||||
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 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)
|
||||
metadata.Outbound = h.Tag()
|
||||
metadata.Destination = destination
|
||||
|
@ -232,7 +236,7 @@ func (h *Outbound) ListenSerialNetworkPacket(ctx context.Context, destination M.
|
|||
} else {
|
||||
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 {
|
||||
return nil, netip.Addr{}, err
|
||||
}
|
||||
|
|
|
@ -59,10 +59,12 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
|
|||
interfaceFinder: control.NewDefaultInterfaceFinder(),
|
||||
autoDetectInterface: routeOptions.AutoDetectInterface,
|
||||
defaultOptions: adapter.NetworkOptions{
|
||||
DefaultInterface: routeOptions.DefaultInterface,
|
||||
DefaultMark: routeOptions.DefaultMark,
|
||||
DefaultNetworkStrategy: C.NetworkStrategy(routeOptions.DefaultNetworkStrategy),
|
||||
DefaultFallbackDelay: time.Duration(routeOptions.DefaultFallbackDelay),
|
||||
BindInterface: routeOptions.DefaultInterface,
|
||||
RoutingMark: routeOptions.DefaultMark,
|
||||
NetworkStrategy: C.NetworkStrategy(routeOptions.DefaultNetworkStrategy),
|
||||
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),
|
||||
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 {
|
||||
return it.Interface.Index == defaultInterface.Index
|
||||
})
|
||||
if networkInterface.Type == "" {
|
||||
if networkInterface.Name == "" {
|
||||
// race
|
||||
return
|
||||
}
|
||||
|
|
|
@ -140,6 +140,8 @@ func (r *RuleActionRoute) String() string {
|
|||
|
||||
type RuleActionRouteOptions struct {
|
||||
NetworkStrategy C.NetworkStrategy
|
||||
NetworkType []C.InterfaceType
|
||||
FallbackNetworkType []C.InterfaceType
|
||||
FallbackDelay time.Duration
|
||||
UDPDisableDomainUnmapping bool
|
||||
UDPConnect bool
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"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)
|
||||
}
|
||||
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.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)
|
||||
}
|
||||
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.allItems = append(rule.allItems, item)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
@ -142,7 +143,7 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
|||
}
|
||||
if networkManager != nil {
|
||||
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.allItems = append(rule.allItems, item)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
@ -12,10 +13,10 @@ var _ RuleItem = (*NetworkTypeItem)(nil)
|
|||
|
||||
type NetworkTypeItem struct {
|
||||
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{
|
||||
networkManager: networkManager,
|
||||
networkType: networkType,
|
||||
|
|
Loading…
Reference in a new issue