Add Linux WI-FI state support

This commit is contained in:
世界
2025-10-07 20:19:17 +08:00
parent f1e48a1cad
commit 766972ce52
17 changed files with 862 additions and 39 deletions
+84 -26
View File
@@ -8,11 +8,13 @@ import (
"os"
"runtime"
"strings"
"sync"
"syscall"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/common/settings"
"github.com/sagernet/sing-box/common/taskmonitor"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
@@ -49,28 +51,31 @@ type NetworkManager struct {
endpoint adapter.EndpointManager
inbound adapter.InboundManager
outbound adapter.OutboundManager
needWIFIState bool
wifiMonitor settings.WIFIMonitor
wifiState adapter.WIFIState
wifiStateMutex sync.RWMutex
started bool
}
func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) {
defaultDomainResolver := common.PtrValueOrDefault(routeOptions.DefaultDomainResolver)
if routeOptions.AutoDetectInterface && !(C.IsLinux || C.IsDarwin || C.IsWindows) {
func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, options option.RouteOptions, dnsOptions option.DNSOptions) (*NetworkManager, error) {
defaultDomainResolver := common.PtrValueOrDefault(options.DefaultDomainResolver)
if options.AutoDetectInterface && !(C.IsLinux || C.IsDarwin || C.IsWindows) {
return nil, E.New("`auto_detect_interface` is only supported on Linux, Windows and macOS")
} else if routeOptions.OverrideAndroidVPN && !C.IsAndroid {
} else if options.OverrideAndroidVPN && !C.IsAndroid {
return nil, E.New("`override_android_vpn` is only supported on Android")
} else if routeOptions.DefaultInterface != "" && !(C.IsLinux || C.IsDarwin || C.IsWindows) {
} else if options.DefaultInterface != "" && !(C.IsLinux || C.IsDarwin || C.IsWindows) {
return nil, E.New("`default_interface` is only supported on Linux, Windows and macOS")
} else if routeOptions.DefaultMark != 0 && !C.IsLinux {
} else if options.DefaultMark != 0 && !C.IsLinux {
return nil, E.New("`default_mark` is only supported on linux")
}
nm := &NetworkManager{
logger: logger,
interfaceFinder: control.NewDefaultInterfaceFinder(),
autoDetectInterface: routeOptions.AutoDetectInterface,
autoDetectInterface: options.AutoDetectInterface,
defaultOptions: adapter.NetworkOptions{
BindInterface: routeOptions.DefaultInterface,
RoutingMark: uint32(routeOptions.DefaultMark),
BindInterface: options.DefaultInterface,
RoutingMark: uint32(options.DefaultMark),
DomainResolver: defaultDomainResolver.Server,
DomainResolveOptions: adapter.DNSQueryOptions{
Strategy: C.DomainStrategy(defaultDomainResolver.Strategy),
@@ -78,27 +83,28 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
RewriteTTL: defaultDomainResolver.RewriteTTL,
ClientSubnet: defaultDomainResolver.ClientSubnet.Build(netip.Prefix{}),
},
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),
NetworkStrategy: (*C.NetworkStrategy)(options.DefaultNetworkStrategy),
NetworkType: common.Map(options.DefaultNetworkType, option.InterfaceType.Build),
FallbackNetworkType: common.Map(options.DefaultFallbackNetworkType, option.InterfaceType.Build),
FallbackDelay: time.Duration(options.DefaultFallbackDelay),
},
pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
endpoint: service.FromContext[adapter.EndpointManager](ctx),
inbound: service.FromContext[adapter.InboundManager](ctx),
outbound: service.FromContext[adapter.OutboundManager](ctx),
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
}
if routeOptions.DefaultNetworkStrategy != nil {
if routeOptions.DefaultInterface != "" {
if options.DefaultNetworkStrategy != nil {
if options.DefaultInterface != "" {
return nil, E.New("`default_network_strategy` is conflict with `default_interface`")
}
if !routeOptions.AutoDetectInterface {
if !options.AutoDetectInterface {
return nil, E.New("`auto_detect_interface` is required by `default_network_strategy`")
}
}
usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil
enforceInterfaceMonitor := routeOptions.AutoDetectInterface
enforceInterfaceMonitor := options.AutoDetectInterface
if !usePlatformDefaultInterfaceMonitor {
networkMonitor, err := tun.NewNetworkUpdateMonitor(logger)
if !((err != nil && !enforceInterfaceMonitor) || errors.Is(err, os.ErrInvalid)) {
@@ -108,7 +114,7 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
nm.networkMonitor = networkMonitor
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(nm.networkMonitor, logger, tun.DefaultInterfaceMonitorOptions{
InterfaceFinder: nm.interfaceFinder,
OverrideAndroidVPN: routeOptions.OverrideAndroidVPN,
OverrideAndroidVPN: options.OverrideAndroidVPN,
UnderNetworkExtension: nm.platformInterface != nil && nm.platformInterface.UnderNetworkExtension(),
})
if err != nil {
@@ -182,11 +188,35 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
}
}
case adapter.StartStatePostStart:
if r.needWIFIState && !(r.platformInterface != nil && r.platformInterface.UsePlatformWIFIMonitor()) {
wifiMonitor, err := settings.NewWIFIMonitor(r.onWIFIStateChanged)
if err != nil {
if err != os.ErrInvalid {
r.logger.Warn(E.Cause(err, "create WIFI monitor"))
}
} else {
r.wifiMonitor = wifiMonitor
err = r.wifiMonitor.Start()
if err != nil {
r.logger.Warn(E.Cause(err, "start WIFI monitor"))
}
}
}
r.started = true
}
return nil
}
func (r *NetworkManager) Initialize(ruleSets []adapter.RuleSet) {
for _, ruleSet := range ruleSets {
metadata := ruleSet.Metadata()
if metadata.ContainsWIFIRule {
r.needWIFIState = true
break
}
}
}
func (r *NetworkManager) Close() error {
monitor := taskmonitor.New(r.logger, C.StopTimeout)
var err error
@@ -218,6 +248,13 @@ func (r *NetworkManager) Close() error {
})
monitor.Finish()
}
if r.wifiMonitor != nil {
monitor.Start("close WIFI monitor")
err = E.Append(err, r.wifiMonitor.Close(), func(err error) error {
return E.Cause(err, "close WIFI monitor")
})
monitor.Finish()
}
return err
}
@@ -375,22 +412,43 @@ func (r *NetworkManager) PackageManager() tun.PackageManager {
return r.packageManager
}
func (r *NetworkManager) NeedWIFIState() bool {
return r.needWIFIState
}
func (r *NetworkManager) WIFIState() adapter.WIFIState {
r.wifiStateMutex.RLock()
defer r.wifiStateMutex.RUnlock()
return r.wifiState
}
func (r *NetworkManager) UpdateWIFIState() {
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)
}
func (r *NetworkManager) onWIFIStateChanged(state adapter.WIFIState) {
r.wifiStateMutex.Lock()
if state != r.wifiState {
r.wifiState = state
r.wifiStateMutex.Unlock()
if state.SSID != "" {
r.logger.Info("WIFI state changed: SSID=", state.SSID, ", BSSID=", state.BSSID)
} else {
r.logger.Info("WIFI disconnected")
}
} else {
r.wifiStateMutex.Unlock()
}
}
func (r *NetworkManager) UpdateWIFIState() {
var state adapter.WIFIState
if r.wifiMonitor != nil {
state = r.wifiMonitor.ReadWIFIState()
} else if r.platformInterface != nil && r.platformInterface.UsePlatformWIFIMonitor() {
state = r.platformInterface.ReadWIFIState()
} else {
return
}
r.onWIFIStateChanged(state)
}
func (r *NetworkManager) ResetNetwork() {
conntrack.Close()
+1 -8
View File
@@ -56,7 +56,6 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.Route
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
}
}
@@ -118,10 +117,8 @@ func (r *Router) Start(stage adapter.StartStage) error {
if metadata.ContainsProcessRule {
needFindProcess = true
}
if metadata.ContainsWIFIRule {
r.needWIFIState = true
}
}
r.network.Initialize(r.ruleSets)
if needFindProcess {
if r.platformInterface != nil && r.platformInterface.UsePlatformConnectionOwnerFinder() {
r.processSearcher = newPlatformSearcher(r.platformInterface)
@@ -194,10 +191,6 @@ func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
return ruleSet, loaded
}
func (r *Router) NeedWIFIState() bool {
return r.needWIFIState
}
func (r *Router) Rules() []adapter.Rule {
return r.rules
}