|
|
@@ -3,9 +3,10 @@ package route
|
|
|
import (
|
|
|
"context"
|
|
|
"errors"
|
|
|
- "net/netip"
|
|
|
+ "net"
|
|
|
"os"
|
|
|
"runtime"
|
|
|
+ "strings"
|
|
|
"syscall"
|
|
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
|
@@ -15,33 +16,41 @@ import (
|
|
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
|
|
"github.com/sagernet/sing-box/option"
|
|
|
"github.com/sagernet/sing-tun"
|
|
|
+ "github.com/sagernet/sing/common"
|
|
|
+ "github.com/sagernet/sing/common/atomic"
|
|
|
"github.com/sagernet/sing/common/control"
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
+ F "github.com/sagernet/sing/common/format"
|
|
|
"github.com/sagernet/sing/common/logger"
|
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
|
"github.com/sagernet/sing/common/winpowrprof"
|
|
|
"github.com/sagernet/sing/service"
|
|
|
"github.com/sagernet/sing/service/pause"
|
|
|
+
|
|
|
+ "golang.org/x/exp/slices"
|
|
|
)
|
|
|
|
|
|
var _ adapter.NetworkManager = (*NetworkManager)(nil)
|
|
|
|
|
|
type NetworkManager struct {
|
|
|
- logger logger.ContextLogger
|
|
|
- interfaceFinder *control.DefaultInterfaceFinder
|
|
|
+ logger logger.ContextLogger
|
|
|
+ interfaceFinder *control.DefaultInterfaceFinder
|
|
|
+ networkInterfaces atomic.TypedValue[[]adapter.NetworkInterface]
|
|
|
+
|
|
|
autoDetectInterface bool
|
|
|
defaultInterface string
|
|
|
defaultMark uint32
|
|
|
autoRedirectOutputMark uint32
|
|
|
- networkMonitor tun.NetworkUpdateMonitor
|
|
|
- interfaceMonitor tun.DefaultInterfaceMonitor
|
|
|
- packageManager tun.PackageManager
|
|
|
- powerListener winpowrprof.EventListener
|
|
|
- pauseManager pause.Manager
|
|
|
- platformInterface platform.Interface
|
|
|
- outboundManager adapter.OutboundManager
|
|
|
- wifiState adapter.WIFIState
|
|
|
- started bool
|
|
|
+
|
|
|
+ networkMonitor tun.NetworkUpdateMonitor
|
|
|
+ interfaceMonitor tun.DefaultInterfaceMonitor
|
|
|
+ packageManager tun.PackageManager
|
|
|
+ powerListener winpowrprof.EventListener
|
|
|
+ pauseManager pause.Manager
|
|
|
+ platformInterface platform.Interface
|
|
|
+ outboundManager adapter.OutboundManager
|
|
|
+ wifiState adapter.WIFIState
|
|
|
+ started bool
|
|
|
}
|
|
|
|
|
|
func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) {
|
|
|
@@ -55,7 +64,7 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
|
|
|
platformInterface: service.FromContext[platform.Interface](ctx),
|
|
|
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
|
|
}
|
|
|
- usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil && nm.platformInterface.UsePlatformDefaultInterfaceMonitor()
|
|
|
+ usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil
|
|
|
enforceInterfaceMonitor := routeOptions.AutoDetectInterface
|
|
|
if !usePlatformDefaultInterfaceMonitor {
|
|
|
networkMonitor, err := tun.NewNetworkUpdateMonitor(logger)
|
|
|
@@ -87,17 +96,17 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
|
|
|
monitor := taskmonitor.New(r.logger, C.StartTimeout)
|
|
|
switch stage {
|
|
|
case adapter.StartStateInitialize:
|
|
|
- if r.interfaceMonitor != nil {
|
|
|
- monitor.Start("initialize interface monitor")
|
|
|
- err := r.interfaceMonitor.Start()
|
|
|
+ if r.networkMonitor != nil {
|
|
|
+ monitor.Start("initialize network monitor")
|
|
|
+ err := r.networkMonitor.Start()
|
|
|
monitor.Finish()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- if r.networkMonitor != nil {
|
|
|
- monitor.Start("initialize network monitor")
|
|
|
- err := r.networkMonitor.Start()
|
|
|
+ if r.interfaceMonitor != nil {
|
|
|
+ monitor.Start("initialize interface monitor")
|
|
|
+ err := r.interfaceMonitor.Start()
|
|
|
monitor.Finish()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
@@ -148,20 +157,6 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
|
|
|
func (r *NetworkManager) Close() error {
|
|
|
monitor := taskmonitor.New(r.logger, C.StopTimeout)
|
|
|
var err error
|
|
|
- if r.interfaceMonitor != nil {
|
|
|
- monitor.Start("close interface monitor")
|
|
|
- err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
|
|
|
- return E.Cause(err, "close interface monitor")
|
|
|
- })
|
|
|
- monitor.Finish()
|
|
|
- }
|
|
|
- if r.networkMonitor != nil {
|
|
|
- monitor.Start("close network monitor")
|
|
|
- err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
|
|
|
- return E.Cause(err, "close network monitor")
|
|
|
- })
|
|
|
- monitor.Finish()
|
|
|
- }
|
|
|
if r.packageManager != nil {
|
|
|
monitor.Start("close package manager")
|
|
|
err = E.Append(err, r.packageManager.Close(), func(err error) error {
|
|
|
@@ -176,6 +171,20 @@ func (r *NetworkManager) Close() error {
|
|
|
})
|
|
|
monitor.Finish()
|
|
|
}
|
|
|
+ if r.interfaceMonitor != nil {
|
|
|
+ monitor.Start("close interface monitor")
|
|
|
+ err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
|
|
|
+ return E.Cause(err, "close interface monitor")
|
|
|
+ })
|
|
|
+ monitor.Finish()
|
|
|
+ }
|
|
|
+ if r.networkMonitor != nil {
|
|
|
+ monitor.Start("close network monitor")
|
|
|
+ err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
|
|
|
+ return E.Cause(err, "close network monitor")
|
|
|
+ })
|
|
|
+ monitor.Finish()
|
|
|
+ }
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
@@ -184,18 +193,75 @@ func (r *NetworkManager) InterfaceFinder() control.InterfaceFinder {
|
|
|
}
|
|
|
|
|
|
func (r *NetworkManager) UpdateInterfaces() error {
|
|
|
- if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
|
|
|
+ if r.platformInterface == nil {
|
|
|
return r.interfaceFinder.Update()
|
|
|
} else {
|
|
|
interfaces, err := r.platformInterface.Interfaces()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- r.interfaceFinder.UpdateInterfaces(interfaces)
|
|
|
+ if C.IsDarwin {
|
|
|
+ err = r.interfaceFinder.Update()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // NEInterface only provides name,index and type
|
|
|
+ interfaces = common.Map(interfaces, func(it adapter.NetworkInterface) adapter.NetworkInterface {
|
|
|
+ iif, _ := r.interfaceFinder.ByIndex(it.Index)
|
|
|
+ if iif != nil {
|
|
|
+ it.Interface = *iif
|
|
|
+ }
|
|
|
+ return it
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ r.interfaceFinder.UpdateInterfaces(common.Map(interfaces, func(it adapter.NetworkInterface) control.Interface { return it.Interface }))
|
|
|
+ }
|
|
|
+ oldInterfaces := r.networkInterfaces.Load()
|
|
|
+ newInterfaces := common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
|
|
|
+ return it.Flags&net.FlagUp != 0
|
|
|
+ })
|
|
|
+ r.networkInterfaces.Store(newInterfaces)
|
|
|
+ if len(newInterfaces) > 0 && !slices.EqualFunc(oldInterfaces, newInterfaces, func(oldInterface adapter.NetworkInterface, newInterface adapter.NetworkInterface) bool {
|
|
|
+ return oldInterface.Interface.Index == newInterface.Interface.Index &&
|
|
|
+ oldInterface.Interface.Name == newInterface.Interface.Name &&
|
|
|
+ oldInterface.Interface.Flags == newInterface.Interface.Flags &&
|
|
|
+ oldInterface.Type == newInterface.Type &&
|
|
|
+ oldInterface.Expensive == newInterface.Expensive &&
|
|
|
+ oldInterface.Constrained == newInterface.Constrained
|
|
|
+ }) {
|
|
|
+ r.logger.Info("updated available networks: ", strings.Join(common.Map(newInterfaces, func(it adapter.NetworkInterface) string {
|
|
|
+ var options []string
|
|
|
+ options = append(options, F.ToString(it.Type))
|
|
|
+ if it.Expensive {
|
|
|
+ options = append(options, "expensive")
|
|
|
+ }
|
|
|
+ if it.Constrained {
|
|
|
+ options = append(options, "constrained")
|
|
|
+ }
|
|
|
+ return F.ToString(it.Name, " (", strings.Join(options, ", "), ")")
|
|
|
+ }), ", "))
|
|
|
+ }
|
|
|
return nil
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func (r *NetworkManager) DefaultNetworkInterface() *adapter.NetworkInterface {
|
|
|
+ iif := r.interfaceMonitor.DefaultInterface()
|
|
|
+ if iif == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ for _, it := range r.networkInterfaces.Load() {
|
|
|
+ if it.Interface.Index == iif.Index {
|
|
|
+ return &it
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return &adapter.NetworkInterface{Interface: *iif}
|
|
|
+}
|
|
|
+
|
|
|
+func (r *NetworkManager) NetworkInterfaces() []adapter.NetworkInterface {
|
|
|
+ return r.networkInterfaces.Load()
|
|
|
+}
|
|
|
+
|
|
|
func (r *NetworkManager) DefaultInterface() string {
|
|
|
return r.defaultInterface
|
|
|
}
|
|
|
@@ -217,18 +283,17 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
|
|
|
}
|
|
|
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
|
|
remoteAddr := M.ParseSocksaddr(address).Addr
|
|
|
- if C.IsLinux {
|
|
|
- interfaceName, interfaceIndex = r.interfaceMonitor.DefaultInterface(remoteAddr)
|
|
|
- if interfaceIndex == -1 {
|
|
|
- err = tun.ErrNoRoute
|
|
|
- }
|
|
|
- } else {
|
|
|
- interfaceIndex = r.interfaceMonitor.DefaultInterfaceIndex(remoteAddr)
|
|
|
- if interfaceIndex == -1 {
|
|
|
- err = tun.ErrNoRoute
|
|
|
+ if remoteAddr.IsValid() {
|
|
|
+ iif, err := r.interfaceFinder.ByAddr(remoteAddr)
|
|
|
+ if err == nil {
|
|
|
+ return iif.Name, iif.Index, nil
|
|
|
}
|
|
|
}
|
|
|
- return
|
|
|
+ defaultInterface := r.interfaceMonitor.DefaultInterface()
|
|
|
+ if defaultInterface == nil {
|
|
|
+ return "", -1, tun.ErrNoRoute
|
|
|
+ }
|
|
|
+ return defaultInterface.Name, defaultInterface.Index, nil
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
@@ -282,6 +347,12 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
|
|
|
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() {
|
|
|
@@ -289,17 +360,24 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
|
|
|
} else {
|
|
|
vpnStatus = "disabled"
|
|
|
}
|
|
|
- r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
|
|
|
+ options = append(options, "vpn "+vpnStatus)
|
|
|
} else {
|
|
|
- r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
|
|
+ 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")
|
|
|
+ }
|
|
|
}
|
|
|
+ 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 == "" && state.BSSID == "" {
|
|
|
- r.logger.Info("updated WIFI state: disconnected")
|
|
|
- } else {
|
|
|
+ if state.SSID != "" {
|
|
|
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
|
|
|
}
|
|
|
}
|
|
|
@@ -309,7 +387,6 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
|
|
|
if !r.started {
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
r.ResetNetwork()
|
|
|
}
|
|
|
|