mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-16 02:22:21 +08:00
Refactor multi networks strategy
This commit is contained in:
parent
2be7482e32
commit
55b2e114f6
|
@ -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,16 @@ 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 defaultOptions.BindInterface != "" {
|
||||||
bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), defaultOptions.DefaultInterface, -1)
|
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)
|
||||||
|
@ -179,6 +188,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 +213,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 +237,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 +260,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 +275,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 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) {
|
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:
|
||||||
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
if len(interfaceType) == 0 {
|
||||||
if defaultIf != nil {
|
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||||
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:
|
||||||
primaryInterfaces = interfaces
|
if len(interfaceType) == 0 {
|
||||||
case C.NetworkStrategyWIFI:
|
primaryInterfaces = interfaces
|
||||||
for _, iif := range interfaces {
|
} else {
|
||||||
if iif.Type == C.InterfaceTypeWIFI {
|
primaryInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
return common.Contains(interfaceType, iif.Type)
|
||||||
} else {
|
})
|
||||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case C.NetworkStrategyCellular:
|
case C.NetworkStrategyFallback:
|
||||||
for _, iif := range interfaces {
|
if len(interfaceType) == 0 {
|
||||||
if iif.Type == C.InterfaceTypeCellular {
|
defaultIf := networkManager.InterfaceMonitor().DefaultInterface()
|
||||||
primaryInterfaces = append(primaryInterfaces, iif)
|
for _, iif := range interfaces {
|
||||||
} else {
|
if iif.Index == defaultIf.Index {
|
||||||
fallbackInterfaces = append(fallbackInterfaces, iif)
|
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:
|
fallbackInterfaces = common.Filter(interfaces, func(iif adapter.NetworkInterface) bool {
|
||||||
for _, iif := range interfaces {
|
return common.Contains(fallbackInterfaceType, iif.Type)
|
||||||
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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -25,7 +25,7 @@ require (
|
||||||
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3
|
||||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.6.0-alpha.8
|
github.com/sagernet/sing v0.6.0-alpha.9
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.1
|
github.com/sagernet/sing-dns v0.4.0-alpha.1
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2
|
github.com/sagernet/sing-quic v0.3.0-rc.2
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -110,8 +110,8 @@ github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.8 h1:j0ghX5QAXH/ozUjsTidMyHclkLw/mpRzHlQagByRsJI=
|
github.com/sagernet/sing v0.6.0-alpha.9 h1:tOeHdRECQwe9R/1edVHbckF/IBoJoGzqhHRnHsNAQb8=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-alpha.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.1 h1:2KlP8DeqtGkULFiZtvG2r7SuoJP6orANFzJwC5vDKvg=
|
github.com/sagernet/sing-dns v0.4.0-alpha.1 h1:2KlP8DeqtGkULFiZtvG2r7SuoJP6orANFzJwC5vDKvg=
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.1/go.mod h1:vgHATsm4wdymwpvBZPei8RY+546iGXS6hlWv2x6YKcM=
|
github.com/sagernet/sing-dns v0.4.0-alpha.1/go.mod h1:vgHATsm4wdymwpvBZPei8RY+546iGXS6hlWv2x6YKcM=
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||||
|
|
|
@ -65,23 +65,25 @@ type DialerOptionsWrapper interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DialerOptions struct {
|
type DialerOptions struct {
|
||||||
Detour string `json:"detour,omitempty"`
|
Detour string `json:"detour,omitempty"`
|
||||||
BindInterface string `json:"bind_interface,omitempty"`
|
BindInterface string `json:"bind_interface,omitempty"`
|
||||||
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"`
|
||||||
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
TCPMultiPath bool `json:"tcp_multi_path,omitempty"`
|
||||||
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
UDPFragment *bool `json:"udp_fragment,omitempty"`
|
||||||
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"`
|
||||||
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"`
|
||||||
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"`
|
||||||
IsWireGuardListener bool `json:"-"`
|
FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"`
|
||||||
|
NetworkFallbackDelay badoption.Duration `json:"network_fallback_delay,omitempty"`
|
||||||
|
IsWireGuardListener bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *DialerOptions) TakeDialerOptions() DialerOptions {
|
func (o *DialerOptions) TakeDialerOptions() DialerOptions {
|
||||||
|
|
|
@ -3,18 +3,20 @@ package option
|
||||||
import "github.com/sagernet/sing/common/json/badoption"
|
import "github.com/sagernet/sing/common/json/badoption"
|
||||||
|
|
||||||
type RouteOptions struct {
|
type RouteOptions struct {
|
||||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||||
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
Geosite *GeositeOptions `json:"geosite,omitempty"`
|
||||||
Rules []Rule `json:"rules,omitempty"`
|
Rules []Rule `json:"rules,omitempty"`
|
||||||
RuleSet []RuleSet `json:"rule_set,omitempty"`
|
RuleSet []RuleSet `json:"rule_set,omitempty"`
|
||||||
Final string `json:"final,omitempty"`
|
Final string `json:"final,omitempty"`
|
||||||
FindProcess bool `json:"find_process,omitempty"`
|
FindProcess bool `json:"find_process,omitempty"`
|
||||||
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 uint32 `json:"default_mark,omitempty"`
|
||||||
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
DefaultNetworkStrategy NetworkStrategy `json:"default_network_strategy,omitempty"`
|
||||||
DefaultFallbackDelay badoption.Duration `json:"default_fallback_delay,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 {
|
type GeoIPOptions struct {
|
||||||
|
|
|
@ -67,42 +67,42 @@ func (r Rule) IsValid() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawDefaultRule struct {
|
type RawDefaultRule struct {
|
||||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||||
IPVersion int `json:"ip_version,omitempty"`
|
IPVersion int `json:"ip_version,omitempty"`
|
||||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||||
Client badoption.Listable[string] `json:"client,omitempty"`
|
Client badoption.Listable[string] `json:"client,omitempty"`
|
||||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||||
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"`
|
||||||
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"`
|
||||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
|
|
||||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||||
|
|
|
@ -68,44 +68,44 @@ func (r DNSRule) IsValid() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawDefaultDNSRule struct {
|
type RawDefaultDNSRule struct {
|
||||||
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
Inbound badoption.Listable[string] `json:"inbound,omitempty"`
|
||||||
IPVersion int `json:"ip_version,omitempty"`
|
IPVersion int `json:"ip_version,omitempty"`
|
||||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||||
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
AuthUser badoption.Listable[string] `json:"auth_user,omitempty"`
|
||||||
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
Protocol badoption.Listable[string] `json:"protocol,omitempty"`
|
||||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||||
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
Geosite badoption.Listable[string] `json:"geosite,omitempty"`
|
||||||
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
SourceGeoIP badoption.Listable[string] `json:"source_geoip,omitempty"`
|
||||||
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
GeoIP badoption.Listable[string] `json:"geoip,omitempty"`
|
||||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||||
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
IPIsPrivate bool `json:"ip_is_private,omitempty"`
|
||||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||||
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
SourceIPIsPrivate bool `json:"source_ip_is_private,omitempty"`
|
||||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||||
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"`
|
||||||
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"`
|
||||||
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"`
|
||||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
|
|
||||||
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
// Deprecated: renamed to rule_set_ip_cidr_match_source
|
||||||
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
|
||||||
|
|
|
@ -146,28 +146,28 @@ func (r HeadlessRule) IsValid() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultHeadlessRule struct {
|
type DefaultHeadlessRule struct {
|
||||||
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
|
||||||
Network badoption.Listable[string] `json:"network,omitempty"`
|
Network badoption.Listable[string] `json:"network,omitempty"`
|
||||||
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
Domain badoption.Listable[string] `json:"domain,omitempty"`
|
||||||
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
|
||||||
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
|
||||||
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
|
||||||
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
|
||||||
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
|
||||||
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
|
||||||
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
|
||||||
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
Port badoption.Listable[uint16] `json:"port,omitempty"`
|
||||||
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
|
||||||
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
|
||||||
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"`
|
||||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
|
|
||||||
DomainMatcher *domain.Matcher `json:"-"`
|
DomainMatcher *domain.Matcher `json:"-"`
|
||||||
SourceIPSet *netipx.IPSet `json:"-"`
|
SourceIPSet *netipx.IPSet `json:"-"`
|
||||||
|
|
|
@ -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: 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 New Issue
Block a user