Add multi network dialing

This commit is contained in:
世界 2024-11-12 19:37:10 +08:00
parent dcb10c21a1
commit bb46cdb2b3
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
30 changed files with 1203 additions and 371 deletions

View file

@ -8,6 +8,7 @@ import (
"runtime"
"strings"
"syscall"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/conntrack"
@ -38,8 +39,7 @@ type NetworkManager struct {
networkInterfaces atomic.TypedValue[[]adapter.NetworkInterface]
autoDetectInterface bool
defaultInterface string
defaultMark uint32
defaultOptions adapter.NetworkOptions
autoRedirectOutputMark uint32
networkMonitor tun.NetworkUpdateMonitor
@ -58,11 +58,23 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
logger: logger,
interfaceFinder: control.NewDefaultInterfaceFinder(),
autoDetectInterface: routeOptions.AutoDetectInterface,
defaultInterface: routeOptions.DefaultInterface,
defaultMark: routeOptions.DefaultMark,
pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: service.FromContext[platform.Interface](ctx),
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
defaultOptions: adapter.NetworkOptions{
DefaultInterface: routeOptions.DefaultInterface,
DefaultMark: routeOptions.DefaultMark,
DefaultNetworkStrategy: C.NetworkStrategy(routeOptions.DefaultNetworkStrategy),
DefaultFallbackDelay: time.Duration(routeOptions.DefaultFallbackDelay),
},
pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: service.FromContext[platform.Interface](ctx),
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
}
if C.NetworkStrategy(routeOptions.DefaultNetworkStrategy) != C.NetworkStrategyDefault {
if routeOptions.DefaultInterface != "" {
return nil, E.New("`default_network_strategy` is conflict with `default_interface`")
}
if !routeOptions.AutoDetectInterface {
return nil, E.New("`auto_detect_interface` is required by `default_network_strategy`")
}
}
usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil
enforceInterfaceMonitor := routeOptions.AutoDetectInterface
@ -81,12 +93,12 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(nm.notifyNetworkUpdate)
interfaceMonitor.RegisterCallback(nm.notifyInterfaceUpdate)
nm.interfaceMonitor = interfaceMonitor
}
} else {
interfaceMonitor := nm.platformInterface.CreateDefaultInterfaceMonitor(logger)
interfaceMonitor.RegisterCallback(nm.notifyNetworkUpdate)
interfaceMonitor.RegisterCallback(nm.notifyInterfaceUpdate)
nm.interfaceMonitor = interfaceMonitor
}
return nm, nil
@ -262,10 +274,6 @@ func (r *NetworkManager) NetworkInterfaces() []adapter.NetworkInterface {
return r.networkInterfaces.Load()
}
func (r *NetworkManager) DefaultInterface() string {
return r.defaultInterface
}
func (r *NetworkManager) AutoDetectInterface() bool {
return r.autoDetectInterface
}
@ -298,8 +306,19 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
}
}
func (r *NetworkManager) DefaultMark() uint32 {
return r.defaultMark
func (r *NetworkManager) ProtectFunc() control.Func {
if r.platformInterface != nil && r.platformInterface.UsePlatformAutoDetectInterfaceControl() {
return func(network, address string, conn syscall.RawConn) error {
return control.Raw(conn, func(fd uintptr) error {
return r.platformInterface.AutoDetectInterfaceControl(int(fd))
})
}
}
return nil
}
func (r *NetworkManager) DefaultOptions() adapter.NetworkOptions {
return r.defaultOptions
}
func (r *NetworkManager) RegisterAutoRedirectOutputMark(mark uint32) error {
@ -341,45 +360,47 @@ func (r *NetworkManager) ResetNetwork() {
}
}
func (r *NetworkManager) notifyNetworkUpdate(event int) {
if event == tun.EventNoRoute {
func (r *NetworkManager) notifyInterfaceUpdate(defaultInterface *control.Interface, flags int) {
if defaultInterface == nil {
r.pauseManager.NetworkPause()
r.logger.Error("missing default interface")
} else {
r.pauseManager.NetworkWake()
defaultInterface := r.DefaultNetworkInterface()
if defaultInterface == nil {
panic("invalid interface context")
}
var options []string
options = append(options, F.ToString("index ", defaultInterface.Index))
if C.IsAndroid && r.platformInterface == nil {
var vpnStatus string
if r.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled"
} else {
vpnStatus = "disabled"
}
options = append(options, "vpn "+vpnStatus)
return
}
r.pauseManager.NetworkWake()
var options []string
options = append(options, F.ToString("index ", defaultInterface.Index))
if C.IsAndroid && r.platformInterface == nil {
var vpnStatus string
if r.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled"
} else {
if defaultInterface.Type != "" {
options = append(options, F.ToString("type ", defaultInterface.Type))
}
if defaultInterface.Expensive {
options = append(options, "expensive")
}
if defaultInterface.Constrained {
options = append(options, "constrained")
}
vpnStatus = "disabled"
}
r.logger.Info("updated default interface ", defaultInterface.Name, ", ", strings.Join(options, ", "))
if r.platformInterface != nil {
state := r.platformInterface.ReadWIFIState()
if state != r.wifiState {
r.wifiState = state
if state.SSID != "" {
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
}
options = append(options, "vpn "+vpnStatus)
} else if r.platformInterface != nil {
networkInterface := common.Find(r.networkInterfaces.Load(), func(it adapter.NetworkInterface) bool {
return it.Interface.Index == defaultInterface.Index
})
if networkInterface.Type == "" {
// race
return
}
options = append(options, F.ToString("type ", networkInterface.Type))
if networkInterface.Expensive {
options = append(options, "expensive")
}
if networkInterface.Constrained {
options = append(options, "constrained")
}
}
r.logger.Info("updated default interface ", defaultInterface.Name, ", ", strings.Join(options, ", "))
if r.platformInterface != nil {
state := r.platformInterface.ReadWIFIState()
if state != r.wifiState {
r.wifiState = state
if state.SSID != "" {
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
}
}
}

View file

@ -424,9 +424,13 @@ match:
}
switch action := currentRule.Action().(type) {
case *rule.RuleActionRoute:
metadata.NetworkStrategy = action.NetworkStrategy
metadata.FallbackDelay = action.FallbackDelay
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
metadata.UDPConnect = action.UDPConnect
case *rule.RuleActionRouteOptions:
metadata.NetworkStrategy = action.NetworkStrategy
metadata.FallbackDelay = action.FallbackDelay
metadata.UDPDisableDomainUnmapping = action.UDPDisableDomainUnmapping
metadata.UDPConnect = action.UDPConnect
case *rule.RuleActionSniff:

View file

@ -30,12 +30,16 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
return &RuleActionRoute{
Outbound: action.RouteOptions.Outbound,
RuleActionRouteOptions: RuleActionRouteOptions{
NetworkStrategy: C.NetworkStrategy(action.RouteOptions.NetworkStrategy),
FallbackDelay: time.Duration(action.RouteOptions.FallbackDelay),
UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
UDPConnect: action.RouteOptions.UDPConnect,
},
}, nil
case C.RuleActionTypeRouteOptions:
return &RuleActionRouteOptions{
NetworkStrategy: C.NetworkStrategy(action.RouteOptionsOptions.NetworkStrategy),
FallbackDelay: time.Duration(action.RouteOptionsOptions.FallbackDelay),
UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
UDPConnect: action.RouteOptionsOptions.UDPConnect,
}, nil
@ -135,6 +139,8 @@ func (r *RuleActionRoute) String() string {
}
type RuleActionRouteOptions struct {
NetworkStrategy C.NetworkStrategy
FallbackDelay time.Duration
UDPDisableDomainUnmapping bool
UDPConnect bool
}