|
@@ -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,20 +412,41 @@ 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() {
|