Files
sing-box-extended-mirror/route/network.go
T

603 lines
19 KiB
Go

package route
import (
"context"
"errors"
"net"
"net/netip"
"os"
"runtime"
"strings"
"sync"
"syscall"
"time"
"github.com/sagernet/sing-box/adapter"
"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"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"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 {
ctx context.Context
logger logger.ContextLogger
router adapter.Router
interfaceFinder *control.DefaultInterfaceFinder
networkInterfaces common.TypedValue[[]adapter.NetworkInterface]
autoDetectInterface bool
defaultOptions adapter.NetworkOptions
autoRedirectOutputMark uint32
networkMonitor tun.NetworkUpdateMonitor
interfaceMonitor tun.DefaultInterfaceMonitor
packageManager tun.PackageManager
powerListener winpowrprof.EventListener
pauseManager pause.Manager
platformInterface adapter.PlatformInterface
connectionManager adapter.ConnectionManager
endpoint adapter.EndpointManager
inbound adapter.InboundManager
outbound adapter.OutboundManager
needWIFIState bool
wifiMonitor settings.WIFIMonitor
wifiState adapter.WIFIState
networkEnvironment uint64
stateAccess sync.RWMutex
environmentUpdateAccess sync.Mutex
environmentUpdateTimer *time.Timer
startedCtx context.Context
interfaceUpdateAccess sync.Mutex
interfaceUpdateCancel context.CancelFunc
networkResetPending bool
resetRunAccess sync.Mutex
powerUpdateAccess sync.Mutex
powerUpdateCancel context.CancelFunc
}
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 options.OverrideAndroidVPN && !C.IsAndroid {
return nil, E.New("`override_android_vpn` is only supported on Android")
} 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 options.DefaultMark != 0 && !C.IsLinux {
return nil, E.New("`default_mark` is only supported on linux")
}
nm := &NetworkManager{
ctx: ctx,
logger: logger,
interfaceFinder: control.NewDefaultInterfaceFinder(),
autoDetectInterface: options.AutoDetectInterface,
defaultOptions: adapter.NetworkOptions{
BindInterface: options.DefaultInterface,
RoutingMark: uint32(options.DefaultMark),
DomainResolver: defaultDomainResolver.Server,
DomainResolveOptions: adapter.DNSQueryOptions{
Strategy: C.DomainStrategy(defaultDomainResolver.Strategy),
Timeout: time.Duration(defaultDomainResolver.Timeout),
DisableCache: defaultDomainResolver.DisableCache,
DisableOptimisticCache: defaultDomainResolver.DisableOptimisticCache,
RewriteTTL: defaultDomainResolver.RewriteTTL,
ClientSubnet: defaultDomainResolver.ClientSubnet.Build(netip.Prefix{}),
},
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),
connectionManager: service.FromContext[adapter.ConnectionManager](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 options.DefaultNetworkStrategy != nil {
if options.DefaultInterface != "" {
return nil, E.New("`default_network_strategy` is conflict with `default_interface`")
}
if !options.AutoDetectInterface {
return nil, E.New("`auto_detect_interface` is required by `default_network_strategy`")
}
}
usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil && nm.platformInterface.UsePlatformDefaultInterfaceMonitor()
enforceInterfaceMonitor := options.AutoDetectInterface
if !usePlatformDefaultInterfaceMonitor {
networkMonitor, err := tun.NewNetworkUpdateMonitor(logger)
if !((err != nil && !enforceInterfaceMonitor) || errors.Is(err, os.ErrInvalid)) {
if err != nil {
return nil, E.Cause(err, "create network monitor")
}
nm.networkMonitor = networkMonitor
networkMonitor.RegisterCallback(nm.postUpdateNetworkEnvironment)
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(nm.networkMonitor, logger, tun.DefaultInterfaceMonitorOptions{
InterfaceFinder: nm.interfaceFinder,
OverrideAndroidVPN: options.OverrideAndroidVPN,
UnderNetworkExtension: nm.platformInterface != nil && nm.platformInterface.UnderNetworkExtension(),
})
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
nm.interfaceMonitor = interfaceMonitor
}
} else {
nm.interfaceMonitor = nm.platformInterface.CreateDefaultInterfaceMonitor(logger)
}
return nm, nil
}
func (r *NetworkManager) Start(stage adapter.StartStage, scope *adapter.Scope) error {
monitor := taskmonitor.New(r.logger, C.StartTimeout)
switch stage {
case adapter.StartStateInitialize:
r.router = service.FromContext[adapter.Router](r.ctx)
scope.Add(func() error {
r.environmentUpdateAccess.Lock()
if r.environmentUpdateTimer != nil {
r.environmentUpdateTimer.Stop()
}
r.environmentUpdateAccess.Unlock()
return nil
})
if r.networkMonitor != nil {
monitor.Start("initialize network monitor")
err := r.networkMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
scope.Add(r.networkMonitor.Close)
}
if r.interfaceMonitor != nil {
monitor.Start("initialize interface monitor")
err := r.interfaceMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
scope.Add(r.interfaceMonitor.Close)
interfaceUpdateElement := r.interfaceMonitor.RegisterCallback(r.notifyInterfaceUpdate)
scope.Add(func() error {
r.interfaceMonitor.UnregisterCallback(interfaceUpdateElement)
return nil
})
}
case adapter.StartStateStart:
if C.IsAndroid && r.platformInterface == nil {
monitor.Start("initialize package manager")
packageManager, err := tun.NewPackageManager(tun.PackageManagerOptions{
Callback: r,
Logger: r.logger,
})
monitor.Finish()
if err != nil {
return E.Cause(err, "create package manager")
}
monitor.Start("start package manager")
err = packageManager.Start()
monitor.Finish()
if err != nil {
r.logger.Warn("initialize package manager: ", err)
} else {
r.packageManager = packageManager
scope.Add(packageManager.Close)
}
}
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
scope.Add(wifiMonitor.Close)
err = r.wifiMonitor.Start()
if err != nil {
r.logger.Warn(E.Cause(err, "start WIFI monitor"))
}
}
}
scope.Add(func() error {
r.resetRunAccess.Lock()
defer r.resetRunAccess.Unlock()
return nil
})
r.interfaceUpdateAccess.Lock()
r.startedCtx = scope.Context()
if r.interfaceMonitor != nil {
r.dispatchInterfaceUpdateLocked()
}
r.interfaceUpdateAccess.Unlock()
if runtime.GOOS == "windows" {
powerListener, err := winpowrprof.NewEventListener(r.notifyWindowsPowerEvent)
if err == nil {
r.powerListener = powerListener
} else {
r.logger.Warn("initialize power listener: ", err)
}
}
if r.powerListener != nil {
monitor.Start("start power listener")
err := r.powerListener.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "start power listener")
}
scope.Add(r.powerListener.Close)
}
}
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) InterfaceFinder() control.InterfaceFinder {
return r.interfaceFinder
}
func (r *NetworkManager) UpdateInterfaces() error {
defer r.postUpdateNetworkEnvironment()
if r.platformInterface == nil || !r.platformInterface.UsePlatformNetworkInterfaces() {
return r.interfaceFinder.Update()
} else {
interfaces, err := r.platformInterface.NetworkInterfaces()
if err != nil {
return err
}
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) AutoDetectInterface() bool {
return r.autoDetectInterface
}
func (r *NetworkManager) AutoDetectInterfaceFunc() 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))
})
}
} else {
if r.interfaceMonitor == nil {
return nil
}
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
remoteAddr := M.ParseSocksaddr(address).Addr
if remoteAddr.IsValid() {
iif, err := r.interfaceFinder.ByAddr(remoteAddr)
if err == nil {
return iif.Name, iif.Index, nil
}
}
defaultInterface := r.interfaceMonitor.DefaultInterface()
if defaultInterface == nil {
return "", -1, tun.ErrNoRoute
}
return defaultInterface.Name, defaultInterface.Index, nil
})
}
}
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 {
if r.autoRedirectOutputMark > 0 {
return E.New("only one auto-redirect can be configured")
}
r.autoRedirectOutputMark = mark
return nil
}
func (r *NetworkManager) AutoRedirectOutputMark() uint32 {
return r.autoRedirectOutputMark
}
func (r *NetworkManager) AutoRedirectOutputMarkFunc() control.Func {
return func(network, address string, conn syscall.RawConn) error {
if r.autoRedirectOutputMark == 0 {
return nil
}
return control.RoutingMark(r.autoRedirectOutputMark)(network, address, conn)
}
}
func (r *NetworkManager) NetworkMonitor() tun.NetworkUpdateMonitor {
return r.networkMonitor
}
func (r *NetworkManager) InterfaceMonitor() tun.DefaultInterfaceMonitor {
return r.interfaceMonitor
}
func (r *NetworkManager) PackageManager() tun.PackageManager {
return r.packageManager
}
func (r *NetworkManager) NeedWIFIState() bool {
return r.needWIFIState
}
func (r *NetworkManager) WIFIState() adapter.WIFIState {
r.stateAccess.RLock()
defer r.stateAccess.RUnlock()
return r.wifiState
}
func (r *NetworkManager) onWIFIStateChanged(state adapter.WIFIState) {
state.BSSID = adapter.NormalizeWIFIBSSID(state.BSSID)
r.stateAccess.Lock()
if state != r.wifiState {
r.wifiState = state
r.stateAccess.Unlock()
r.postUpdateNetworkEnvironment()
if state.SSID != "" {
r.logger.Info("WIFI state changed: SSID=", state.SSID, ", BSSID=", state.BSSID)
} else {
r.logger.Info("WIFI disconnected")
}
} else {
r.stateAccess.Unlock()
}
}
func (r *NetworkManager) UpdateWIFIState(ctx context.Context) {
var state adapter.WIFIState
if r.wifiMonitor != nil {
state = r.wifiMonitor.ReadWIFIState(ctx)
} else if r.platformInterface != nil && r.platformInterface.UsePlatformWIFIMonitor() {
state = r.platformInterface.ReadWIFIState(ctx)
} else {
return
}
r.onWIFIStateChanged(state)
}
func (r *NetworkManager) ResetNetwork(ctx context.Context) {
if r.connectionManager != nil {
r.connectionManager.CloseAll()
}
for _, endpoint := range r.endpoint.Endpoints() {
listener, isListener := endpoint.(adapter.InterfaceUpdateListener)
if isListener {
listener.InterfaceUpdated(ctx)
}
}
for _, inbound := range r.inbound.Inbounds() {
listener, isListener := inbound.(adapter.InterfaceUpdateListener)
if isListener {
listener.InterfaceUpdated(ctx)
}
}
for _, outbound := range r.outbound.Outbounds() {
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener {
listener.InterfaceUpdated(ctx)
}
}
r.router.ResetNetwork()
}
func (r *NetworkManager) notifyInterfaceUpdate(_ *control.Interface, _ int) {
r.interfaceUpdateAccess.Lock()
defer r.interfaceUpdateAccess.Unlock()
r.networkResetPending = true
if r.startedCtx != nil {
r.dispatchInterfaceUpdateLocked()
}
}
func (r *NetworkManager) dispatchInterfaceUpdateLocked() {
defaultInterface := r.interfaceMonitor.DefaultInterface()
if defaultInterface == nil {
r.pauseManager.NetworkPause()
r.logger.Error("missing default interface")
return
}
r.pauseManager.NetworkWake()
if r.interfaceUpdateCancel != nil {
r.interfaceUpdateCancel()
}
updateContext, updateCancel := context.WithCancel(r.startedCtx)
r.interfaceUpdateCancel = updateCancel
go r.updateInterface(updateContext, defaultInterface)
}
func (r *NetworkManager) updateInterface(ctx context.Context, defaultInterface *control.Interface) {
r.resetRunAccess.Lock()
defer r.resetRunAccess.Unlock()
if ctx.Err() != nil {
return
}
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)
} else if r.platformInterface != nil && r.platformInterface.UsePlatformNetworkInterfaces() {
networkInterface := common.Find(r.networkInterfaces.Load(), func(it adapter.NetworkInterface) bool {
return it.Interface.Index == defaultInterface.Index
})
if networkInterface.Name == "" {
// 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, ", "))
r.UpdateWIFIState(ctx)
if ctx.Err() != nil {
return
}
r.updateNetworkEnvironment()
r.interfaceUpdateAccess.Lock()
resetNetwork := ctx.Err() == nil && r.networkResetPending
if resetNetwork {
r.networkResetPending = false
}
r.interfaceUpdateAccess.Unlock()
if resetNetwork {
r.ResetNetwork(ctx)
}
}
func (r *NetworkManager) notifyWindowsPowerEvent(event int) {
switch event {
case winpowrprof.EVENT_SUSPEND:
r.pauseManager.DevicePause()
r.cancelPowerUpdate()
r.ResetNetwork(r.startedCtx)
case winpowrprof.EVENT_RESUME:
if !r.pauseManager.IsDevicePaused() {
return
}
fallthrough
case winpowrprof.EVENT_RESUME_AUTOMATIC:
r.pauseManager.DeviceWake()
updateContext, updateCancel := context.WithCancel(r.startedCtx)
r.powerUpdateAccess.Lock()
previousCancel := r.powerUpdateCancel
r.powerUpdateCancel = updateCancel
r.powerUpdateAccess.Unlock()
if previousCancel != nil {
previousCancel()
}
go func() {
r.resetRunAccess.Lock()
defer r.resetRunAccess.Unlock()
if updateContext.Err() != nil {
return
}
r.ResetNetwork(updateContext)
}()
}
}
func (r *NetworkManager) cancelPowerUpdate() {
r.powerUpdateAccess.Lock()
previousCancel := r.powerUpdateCancel
r.powerUpdateCancel = nil
r.powerUpdateAccess.Unlock()
if previousCancel != nil {
previousCancel()
}
}
func (r *NetworkManager) OnPackagesUpdated(packages int, sharedUsers int) {
r.logger.Info("updated packages list: ", packages, " packages, ", sharedUsers, " shared users")
}