refactor: Modular network manager

This commit is contained in:
世界 2024-11-10 12:11:21 +08:00
parent 6f2af49154
commit 9229769d86
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
35 changed files with 615 additions and 606 deletions

View file

@ -52,13 +52,13 @@ func (m *Manager) Start(stage adapter.StartStage) error {
func (m *Manager) Close() error { func (m *Manager) Close() error {
m.access.Lock() m.access.Lock()
defer m.access.Unlock()
if !m.started { if !m.started {
panic("not started") return nil
} }
m.started = false m.started = false
inbounds := m.inbounds inbounds := m.inbounds
m.inbounds = nil m.inbounds = nil
m.access.Unlock()
monitor := taskmonitor.New(m.logger, C.StopTimeout) monitor := taskmonitor.New(m.logger, C.StopTimeout)
var err error var err error
for _, inbound := range inbounds { for _, inbound := range inbounds {

23
adapter/network.go Normal file
View file

@ -0,0 +1,23 @@
package adapter
import (
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
)
type NetworkManager interface {
NewService
InterfaceFinder() control.InterfaceFinder
UpdateInterfaces() error
DefaultInterface() string
AutoDetectInterface() bool
AutoDetectInterfaceFunc() control.Func
DefaultMark() uint32
RegisterAutoRedirectOutputMark(mark uint32) error
AutoRedirectOutputMark() uint32
NetworkMonitor() tun.NetworkUpdateMonitor
InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager
WIFIState() WIFIState
ResetNetwork()
}

View file

@ -48,16 +48,17 @@ func (m *Manager) Initialize(defaultOutboundFallback adapter.Outbound) {
func (m *Manager) Start(stage adapter.StartStage) error { func (m *Manager) Start(stage adapter.StartStage) error {
m.access.Lock() m.access.Lock()
defer m.access.Unlock()
if m.started && m.stage >= stage { if m.started && m.stage >= stage {
panic("already started") panic("already started")
} }
m.started = true m.started = true
m.stage = stage m.stage = stage
outbounds := m.outbounds
m.access.Unlock()
if stage == adapter.StartStateStart { if stage == adapter.StartStateStart {
m.startOutbounds() return m.startOutbounds(outbounds)
} else { } else {
for _, outbound := range m.outbounds { for _, outbound := range outbounds {
err := adapter.LegacyStart(outbound, stage) err := adapter.LegacyStart(outbound, stage)
if err != nil { if err != nil {
return E.Cause(err, stage.Action(), " outbound/", outbound.Type(), "[", outbound.Tag(), "]") return E.Cause(err, stage.Action(), " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
@ -67,13 +68,13 @@ func (m *Manager) Start(stage adapter.StartStage) error {
return nil return nil
} }
func (m *Manager) startOutbounds() error { func (m *Manager) startOutbounds(outbounds []adapter.Outbound) error {
monitor := taskmonitor.New(m.logger, C.StartTimeout) monitor := taskmonitor.New(m.logger, C.StartTimeout)
started := make(map[string]bool) started := make(map[string]bool)
for { for {
canContinue := false canContinue := false
startOne: startOne:
for _, outboundToStart := range m.outbounds { for _, outboundToStart := range outbounds {
outboundTag := outboundToStart.Tag() outboundTag := outboundToStart.Tag()
if started[outboundTag] { if started[outboundTag] {
continue continue
@ -97,13 +98,13 @@ func (m *Manager) startOutbounds() error {
} }
} }
} }
if len(started) == len(m.outbounds) { if len(started) == len(outbounds) {
break break
} }
if canContinue { if canContinue {
continue continue
} }
currentOutbound := common.Find(m.outbounds, func(it adapter.Outbound) bool { currentOutbound := common.Find(outbounds, func(it adapter.Outbound) bool {
return !started[it.Tag()] return !started[it.Tag()]
}) })
var lintOutbound func(oTree []string, oCurrent adapter.Outbound) error var lintOutbound func(oTree []string, oCurrent adapter.Outbound) error
@ -114,7 +115,9 @@ func (m *Manager) startOutbounds() error {
if common.Contains(oTree, problemOutboundTag) { if common.Contains(oTree, problemOutboundTag) {
return E.New("circular outbound dependency: ", strings.Join(oTree, " -> "), " -> ", problemOutboundTag) return E.New("circular outbound dependency: ", strings.Join(oTree, " -> "), " -> ", problemOutboundTag)
} }
m.access.Lock()
problemOutbound := m.outboundByTag[problemOutboundTag] problemOutbound := m.outboundByTag[problemOutboundTag]
m.access.Unlock()
if problemOutbound == nil { if problemOutbound == nil {
return E.New("dependency[", problemOutboundTag, "] not found for outbound[", oCurrent.Tag(), "]") return E.New("dependency[", problemOutboundTag, "] not found for outbound[", oCurrent.Tag(), "]")
} }
@ -129,7 +132,8 @@ func (m *Manager) Close() error {
monitor := taskmonitor.New(m.logger, C.StopTimeout) monitor := taskmonitor.New(m.logger, C.StopTimeout)
m.access.Lock() m.access.Lock()
if !m.started { if !m.started {
panic("not started") m.access.Unlock()
return nil
} }
m.started = false m.started = false
outbounds := m.outbounds outbounds := m.outbounds

View file

@ -10,8 +10,6 @@ import (
"github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geoip"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/common/x/list"
@ -31,28 +29,13 @@ type Router interface {
GeoIPReader() *geoip.Reader GeoIPReader() *geoip.Reader
LoadGeosite(code string) (Rule, error) LoadGeosite(code string) (Rule, error)
RuleSet(tag string) (RuleSet, bool) RuleSet(tag string) (RuleSet, bool)
NeedWIFIState() bool NeedWIFIState() bool
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error) Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
ClearDNSCache() ClearDNSCache()
InterfaceFinder() control.InterfaceFinder
UpdateInterfaces() error
DefaultInterface() string
AutoDetectInterface() bool
AutoDetectInterfaceFunc() control.Func
DefaultMark() uint32
RegisterAutoRedirectOutputMark(mark uint32) error
AutoRedirectOutputMark() uint32
NetworkMonitor() tun.NetworkUpdateMonitor
InterfaceMonitor() tun.DefaultInterfaceMonitor
PackageManager() tun.PackageManager
WIFIState() WIFIState
Rules() []Rule Rules() []Rule
ClashServer() ClashServer ClashServer() ClashServer
@ -61,7 +44,7 @@ type Router interface {
V2RayServer() V2RayServer V2RayServer() V2RayServer
SetV2RayServer(server V2RayServer) SetV2RayServer(server V2RayServer)
ResetNetwork() error ResetNetwork()
} }
// Deprecated: Use ConnectionRouterEx instead. // Deprecated: Use ConnectionRouterEx instead.

56
box.go
View file

@ -34,6 +34,7 @@ type Box struct {
router adapter.Router router adapter.Router
inbound *inbound.Manager inbound *inbound.Manager
outbound *outbound.Manager outbound *outbound.Manager
network *route.NetworkManager
logFactory log.Factory logFactory log.Factory
logger log.ContextLogger logger log.ContextLogger
preServices1 map[string]adapter.Service preServices1 map[string]adapter.Service
@ -109,20 +110,18 @@ func New(options Options) (*Box, error) {
return nil, E.Cause(err, "create log factory") return nil, E.Cause(err, "create log factory")
} }
routeOptions := common.PtrValueOrDefault(options.Route) routeOptions := common.PtrValueOrDefault(options.Route)
inboundManager := inbound.NewManager(logFactory.NewLogger("inbound-manager"), inboundRegistry) inboundManager := inbound.NewManager(logFactory.NewLogger("inbound"), inboundRegistry)
outboundManager := outbound.NewManager(logFactory.NewLogger("outbound-manager"), outboundRegistry, routeOptions.Final) outboundManager := outbound.NewManager(logFactory.NewLogger("outbound"), outboundRegistry, routeOptions.Final)
ctx = service.ContextWith[adapter.InboundManager](ctx, inboundManager) ctx = service.ContextWith[adapter.InboundManager](ctx, inboundManager)
ctx = service.ContextWith[adapter.OutboundManager](ctx, outboundManager) ctx = service.ContextWith[adapter.OutboundManager](ctx, outboundManager)
router, err := route.NewRouter( networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions)
ctx,
logFactory,
routeOptions,
common.PtrValueOrDefault(options.DNS),
common.PtrValueOrDefault(options.NTP),
options.Inbounds,
)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse route options") return nil, E.Cause(err, "initialize network manager")
}
ctx = service.ContextWith[adapter.NetworkManager](ctx, networkManager)
router, err := route.NewRouter(ctx, logFactory, routeOptions, common.PtrValueOrDefault(options.DNS), common.PtrValueOrDefault(options.NTP))
if err != nil {
return nil, E.Cause(err, "initialize router")
} }
for i, inboundOptions := range options.Inbounds { for i, inboundOptions := range options.Inbounds {
var tag string var tag string
@ -177,11 +176,8 @@ func New(options Options) (*Box, error) {
option.DirectOutboundOptions{}, option.DirectOutboundOptions{},
), ),
)) ))
if err != nil {
return nil, err
}
if platformInterface != nil { if platformInterface != nil {
err = platformInterface.Initialize(ctx, router) err = platformInterface.Initialize(networkManager)
if err != nil { if err != nil {
return nil, E.Cause(err, "initialize platform interface") return nil, E.Cause(err, "initialize platform interface")
} }
@ -219,6 +215,7 @@ func New(options Options) (*Box, error) {
router: router, router: router,
inbound: inboundManager, inbound: inboundManager,
outbound: outboundManager, outbound: outboundManager,
network: networkManager,
createdAt: createdAt, createdAt: createdAt,
logFactory: logFactory, logFactory: logFactory,
logger: logFactory.Logger(), logger: logFactory.Logger(),
@ -295,6 +292,10 @@ func (s *Box) preStart() error {
} }
} }
} }
err = s.network.Start(adapter.StartStateInitialize)
if err != nil {
return E.Cause(err, "initialize network manager")
}
err = s.router.Start(adapter.StartStateInitialize) err = s.router.Start(adapter.StartStateInitialize)
if err != nil { if err != nil {
return E.Cause(err, "initialize router") return E.Cause(err, "initialize router")
@ -303,6 +304,10 @@ func (s *Box) preStart() error {
if err != nil { if err != nil {
return err return err
} }
err = s.network.Start(adapter.StartStateStart)
if err != nil {
return err
}
return s.router.Start(adapter.StartStateStart) return s.router.Start(adapter.StartStateStart)
} }
@ -337,6 +342,10 @@ func (s *Box) start() error {
if err != nil { if err != nil {
return err return err
} }
err = s.network.Start(adapter.StartStatePostStart)
if err != nil {
return err
}
err = s.router.Start(adapter.StartStatePostStart) err = s.router.Start(adapter.StartStatePostStart)
if err != nil { if err != nil {
return err return err
@ -345,6 +354,10 @@ func (s *Box) start() error {
if err != nil { if err != nil {
return err return err
} }
err = s.network.Start(adapter.StartStateStarted)
if err != nil {
return err
}
err = s.router.Start(adapter.StartStateStarted) err = s.router.Start(adapter.StartStateStarted)
if err != nil { if err != nil {
return err return err
@ -378,13 +391,8 @@ func (s *Box) Close() error {
} }
errors = E.Errors(errors, s.inbound.Close()) errors = E.Errors(errors, s.inbound.Close())
errors = E.Errors(errors, s.outbound.Close()) errors = E.Errors(errors, s.outbound.Close())
monitor.Start("close router") errors = E.Errors(errors, s.network.Close())
if err := common.Close(s.router); err != nil { errors = E.Errors(errors, s.router.Close())
errors = E.Append(errors, err, func(err error) error {
return E.Cause(err, "close router")
})
}
monitor.Finish()
for serviceName, service := range s.preServices1 { for serviceName, service := range s.preServices1 {
monitor.Start("close ", serviceName) monitor.Start("close ", serviceName)
errors = E.Append(errors, service.Close(), func(err error) error { errors = E.Append(errors, service.Close(), func(err error) error {
@ -415,6 +423,10 @@ func (s *Box) Outbound() adapter.OutboundManager {
return s.outbound return s.outbound
} }
func (s *Box) Network() adapter.NetworkManager {
return s.network
}
func (s *Box) Router() adapter.Router { func (s *Box) Router() adapter.Router {
return s.router return s.router
} }

View file

@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"context"
"io" "io"
"os" "os"
@ -83,7 +84,7 @@ func ruleSetMatch(sourcePath string, domain string) error {
} }
for i, ruleOptions := range plainRuleSet.Rules { for i, ruleOptions := range plainRuleSet.Rules {
var currentRule adapter.HeadlessRule var currentRule adapter.HeadlessRule
currentRule, err = rule.NewHeadlessRule(nil, ruleOptions) currentRule, err = rule.NewHeadlessRule(context.Background(), ruleOptions)
if err != nil { if err != nil {
return E.Cause(err, "parse rule_set.rules.[", i, "]") return E.Cause(err, "parse rule_set.rules.[", i, "]")
} }

View file

@ -29,31 +29,31 @@ type DefaultDialer struct {
isWireGuardListener bool isWireGuardListener bool
} }
func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) { func NewDefault(networkManager adapter.NetworkManager, options option.DialerOptions) (*DefaultDialer, error) {
var dialer net.Dialer var dialer net.Dialer
var listener net.ListenConfig var listener net.ListenConfig
if options.BindInterface != "" { if options.BindInterface != "" {
var interfaceFinder control.InterfaceFinder var interfaceFinder control.InterfaceFinder
if router != nil { if networkManager != nil {
interfaceFinder = router.InterfaceFinder() interfaceFinder = networkManager.InterfaceFinder()
} else { } else {
interfaceFinder = control.NewDefaultInterfaceFinder() interfaceFinder = control.NewDefaultInterfaceFinder()
} }
bindFunc := control.BindToInterface(interfaceFinder, options.BindInterface, -1) bindFunc := control.BindToInterface(interfaceFinder, options.BindInterface, -1)
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} else if router != nil && router.AutoDetectInterface() { } else if networkManager != nil && networkManager.AutoDetectInterface() {
bindFunc := router.AutoDetectInterfaceFunc() bindFunc := networkManager.AutoDetectInterfaceFunc()
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} else if router != nil && router.DefaultInterface() != "" { } else if networkManager != nil && networkManager.DefaultInterface() != "" {
bindFunc := control.BindToInterface(router.InterfaceFinder(), router.DefaultInterface(), -1) bindFunc := control.BindToInterface(networkManager.InterfaceFinder(), networkManager.DefaultInterface(), -1)
dialer.Control = control.Append(dialer.Control, bindFunc) dialer.Control = control.Append(dialer.Control, bindFunc)
listener.Control = control.Append(listener.Control, bindFunc) listener.Control = control.Append(listener.Control, bindFunc)
} }
var autoRedirectOutputMark uint32 var autoRedirectOutputMark uint32
if router != nil { if networkManager != nil {
autoRedirectOutputMark = router.AutoRedirectOutputMark() autoRedirectOutputMark = networkManager.AutoRedirectOutputMark()
} }
if autoRedirectOutputMark > 0 { if autoRedirectOutputMark > 0 {
dialer.Control = control.Append(dialer.Control, control.RoutingMark(autoRedirectOutputMark)) dialer.Control = control.Append(dialer.Control, control.RoutingMark(autoRedirectOutputMark))
@ -65,9 +65,9 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
if autoRedirectOutputMark > 0 { if autoRedirectOutputMark > 0 {
return nil, E.New("`auto_redirect` with `route_[_exclude]_address_set is conflict with `routing_mark`") return nil, E.New("`auto_redirect` with `route_[_exclude]_address_set is conflict with `routing_mark`")
} }
} else if router != nil && router.DefaultMark() > 0 { } else if networkManager != nil && networkManager.DefaultMark() > 0 {
dialer.Control = control.Append(dialer.Control, control.RoutingMark(router.DefaultMark())) dialer.Control = control.Append(dialer.Control, control.RoutingMark(networkManager.DefaultMark()))
listener.Control = control.Append(listener.Control, control.RoutingMark(router.DefaultMark())) listener.Control = control.Append(listener.Control, control.RoutingMark(networkManager.DefaultMark()))
if autoRedirectOutputMark > 0 { if autoRedirectOutputMark > 0 {
return nil, E.New("`auto_redirect` with `route_[_exclude]_address_set is conflict with `default_mark`") return nil, E.New("`auto_redirect` with `route_[_exclude]_address_set is conflict with `default_mark`")
} }

View file

@ -13,16 +13,16 @@ import (
) )
func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) { func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) {
router := service.FromContext[adapter.Router](ctx) networkManager := service.FromContext[adapter.NetworkManager](ctx)
if options.IsWireGuardListener { if options.IsWireGuardListener {
return NewDefault(router, options) return NewDefault(networkManager, options)
} }
var ( var (
dialer N.Dialer dialer N.Dialer
err error err error
) )
if options.Detour == "" { if options.Detour == "" {
dialer, err = NewDefault(router, options) dialer, err = NewDefault(networkManager, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -33,10 +33,12 @@ func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) {
} }
dialer = NewDetour(outboundManager, options.Detour) dialer = NewDetour(outboundManager, options.Detour)
} }
if router == nil { if networkManager == nil {
return NewDefault(router, options) return NewDefault(networkManager, options)
} }
if options.Detour == "" { if options.Detour == "" {
router := service.FromContext[adapter.Router](ctx)
if router != nil {
dialer = NewResolveDialer( dialer = NewResolveDialer(
router, router,
dialer, dialer,
@ -44,5 +46,6 @@ func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) {
dns.DomainStrategy(options.DomainStrategy), dns.DomainStrategy(options.DomainStrategy),
time.Duration(options.FallbackDelay)) time.Duration(options.FallbackDelay))
} }
}
return dialer, nil return dialer, nil
} }

View file

@ -25,7 +25,7 @@ type DarwinSystemProxy struct {
} }
func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool) (*DarwinSystemProxy, error) { func NewSystemProxy(ctx context.Context, serverAddr M.Socksaddr, supportSOCKS bool) (*DarwinSystemProxy, error) {
interfaceMonitor := service.FromContext[adapter.Router](ctx).InterfaceMonitor() interfaceMonitor := service.FromContext[adapter.NetworkManager](ctx).InterfaceMonitor()
if interfaceMonitor == nil { if interfaceMonitor == nil {
return nil, E.New("missing interface monitor") return nil, E.New("missing interface monitor")
} }

View file

@ -109,7 +109,7 @@ func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
func writeGroups(writer io.Writer, boxService *BoxService) error { func writeGroups(writer io.Writer, boxService *BoxService) error {
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx) historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx) cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx)
outbounds := boxService.instance.Router().Outbounds() outbounds := boxService.instance.Outbound().Outbounds()
var iGroups []adapter.OutboundGroup var iGroups []adapter.OutboundGroup
for _, it := range outbounds { for _, it := range outbounds {
if group, isGroup := it.(adapter.OutboundGroup); isGroup { if group, isGroup := it.(adapter.OutboundGroup); isGroup {
@ -130,7 +130,7 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
} }
for _, itemTag := range iGroup.All() { for _, itemTag := range iGroup.All() {
itemOutbound, isLoaded := boxService.instance.Router().Outbound(itemTag) itemOutbound, isLoaded := boxService.instance.Outbound().Outbound(itemTag)
if !isLoaded { if !isLoaded {
continue continue
} }

View file

@ -43,7 +43,7 @@ func (s *CommandServer) handleSelectOutbound(conn net.Conn) error {
if service == nil { if service == nil {
return writeError(conn, E.New("service not ready")) return writeError(conn, E.New("service not ready"))
} }
outboundGroup, isLoaded := service.instance.Router().Outbound(groupTag) outboundGroup, isLoaded := service.instance.Outbound().Outbound(groupTag)
if !isLoaded { if !isLoaded {
return writeError(conn, E.New("selector not found: ", groupTag)) return writeError(conn, E.New("selector not found: ", groupTag))
} }

View file

@ -41,7 +41,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
if serviceNow == nil { if serviceNow == nil {
return nil return nil
} }
abstractOutboundGroup, isLoaded := serviceNow.instance.Router().Outbound(groupTag) abstractOutboundGroup, isLoaded := serviceNow.instance.Outbound().Outbound(groupTag)
if !isLoaded { if !isLoaded {
return writeError(conn, E.New("outbound group not found: ", groupTag)) return writeError(conn, E.New("outbound group not found: ", groupTag))
} }
@ -55,7 +55,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
} else { } else {
historyStorage := service.PtrFromContext[urltest.HistoryStorage](serviceNow.ctx) historyStorage := service.PtrFromContext[urltest.HistoryStorage](serviceNow.ctx)
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound { outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
itOutbound, _ := serviceNow.instance.Router().Outbound(it) itOutbound, _ := serviceNow.instance.Outbound().Outbound(it)
return itOutbound return itOutbound
}), func(it adapter.Outbound) bool { }), func(it adapter.Outbound) bool {
if it == nil { if it == nil {

View file

@ -50,7 +50,7 @@ func CheckConfig(configContent string) error {
type platformInterfaceStub struct{} type platformInterfaceStub struct{}
func (s *platformInterfaceStub) Initialize(ctx context.Context, router adapter.Router) error { func (s *platformInterfaceStub) Initialize(networkManager adapter.NetworkManager) error {
return nil return nil
} }

View file

@ -115,7 +115,7 @@ func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName s
err = m.updateInterfaces() err = m.updateInterfaces()
} }
if err == nil { if err == nil {
err = m.router.UpdateInterfaces() err = m.networkManager.UpdateInterfaces()
} }
if err != nil { if err != nil {
m.logger.Error(E.Cause(err, "update interfaces")) m.logger.Error(E.Cause(err, "update interfaces"))

View file

@ -1,8 +1,6 @@
package platform package platform
import ( import (
"context"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@ -12,7 +10,7 @@ import (
) )
type Interface interface { type Interface interface {
Initialize(ctx context.Context, router adapter.Router) error Initialize(networkManager adapter.NetworkManager) error
UsePlatformAutoDetectInterfaceControl() bool UsePlatformAutoDetectInterfaceControl() bool
AutoDetectInterfaceControl(fd int) error AutoDetectInterfaceControl(fd int) error
OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)

View file

@ -106,11 +106,11 @@ var (
type platformInterfaceWrapper struct { type platformInterfaceWrapper struct {
iif PlatformInterface iif PlatformInterface
useProcFS bool useProcFS bool
router adapter.Router networkManager adapter.NetworkManager
} }
func (w *platformInterfaceWrapper) Initialize(ctx context.Context, router adapter.Router) error { func (w *platformInterfaceWrapper) Initialize(networkManager adapter.NetworkManager) error {
w.router = router w.networkManager = networkManager
return nil return nil
} }

View file

@ -29,5 +29,5 @@ func (s *BoxService) Wake() {
} }
func (s *BoxService) ResetNetwork() { func (s *BoxService) ResetNetwork() {
_ = s.instance.Router().ResetNetwork() s.instance.Router().ResetNetwork()
} }

View file

@ -13,7 +13,7 @@ import (
) )
func init() { func init() {
experimental.RegisterClashServerConstructor(func(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) { experimental.RegisterClashServerConstructor(func(ctx context.Context, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`) return nil, E.New(`clash api is not included in this build, rebuild with -tags with_clash_api`)
}) })
} }

View file

@ -11,16 +11,16 @@ import (
) )
type loopBackDetector struct { type loopBackDetector struct {
router adapter.Router networkManager adapter.NetworkManager
connAccess sync.RWMutex connAccess sync.RWMutex
packetConnAccess sync.RWMutex packetConnAccess sync.RWMutex
connMap map[netip.AddrPort]netip.AddrPort connMap map[netip.AddrPort]netip.AddrPort
packetConnMap map[uint16]uint16 packetConnMap map[uint16]uint16
} }
func newLoopBackDetector(router adapter.Router) *loopBackDetector { func newLoopBackDetector(networkManager adapter.NetworkManager) *loopBackDetector {
return &loopBackDetector{ return &loopBackDetector{
router: router, networkManager: networkManager,
connMap: make(map[netip.AddrPort]netip.AddrPort), connMap: make(map[netip.AddrPort]netip.AddrPort),
packetConnMap: make(map[uint16]uint16), packetConnMap: make(map[uint16]uint16),
} }
@ -33,7 +33,7 @@ func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
} }
if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn { if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
if !source.Addr().IsLoopback() { if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr()) _, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil { if err != nil {
return conn return conn
} }
@ -59,7 +59,7 @@ func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn, destination M.Soc
return conn return conn
} }
if !source.Addr().IsLoopback() { if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr()) _, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil { if err != nil {
return conn return conn
} }
@ -82,7 +82,7 @@ func (l *loopBackDetector) CheckPacketConn(source netip.AddrPort, local netip.Ad
return false return false
} }
if !source.Addr().IsLoopback() { if !source.Addr().IsLoopback() {
_, err := l.router.InterfaceFinder().InterfaceByAddr(source.Addr()) _, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
if err != nil { if err != nil {
return false return false
} }

View file

@ -39,6 +39,7 @@ type Inbound struct {
tag string tag string
ctx context.Context ctx context.Context
router adapter.Router router adapter.Router
networkManager adapter.NetworkManager
logger log.ContextLogger logger log.ContextLogger
// Deprecated // Deprecated
inboundOptions option.InboundOptions inboundOptions option.InboundOptions
@ -168,11 +169,12 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
if outputMark == 0 { if outputMark == 0 {
outputMark = tun.DefaultAutoRedirectOutputMark outputMark = tun.DefaultAutoRedirectOutputMark
} }
networkManager := service.FromContext[adapter.NetworkManager](ctx)
inbound := &Inbound{ inbound := &Inbound{
tag: tag, tag: tag,
ctx: ctx, ctx: ctx,
router: router, router: router,
networkManager: networkManager,
logger: logger, logger: logger,
inboundOptions: options.InboundOptions, inboundOptions: options.InboundOptions,
tunOptions: tun.Options{ tunOptions: tun.Options{
@ -198,7 +200,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
IncludeAndroidUser: options.IncludeAndroidUser, IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage, IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage, ExcludePackage: options.ExcludePackage,
InterfaceMonitor: router.InterfaceMonitor(), InterfaceMonitor: networkManager.InterfaceMonitor(),
}, },
endpointIndependentNat: options.EndpointIndependentNat, endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: udpTimeout, udpTimeout: udpTimeout,
@ -216,8 +218,8 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
Context: ctx, Context: ctx,
Handler: (*autoRedirectHandler)(inbound), Handler: (*autoRedirectHandler)(inbound),
Logger: logger, Logger: logger,
NetworkMonitor: router.NetworkMonitor(), NetworkMonitor: networkManager.NetworkMonitor(),
InterfaceFinder: router.InterfaceFinder(), InterfaceFinder: networkManager.InterfaceFinder(),
TableName: "sing-box", TableName: "sing-box",
DisableNFTables: dErr == nil && disableNFTables, DisableNFTables: dErr == nil && disableNFTables,
RouteAddressSet: &inbound.routeAddressSet, RouteAddressSet: &inbound.routeAddressSet,
@ -248,7 +250,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
} }
if markMode { if markMode {
inbound.tunOptions.AutoRedirectMarkMode = true inbound.tunOptions.AutoRedirectMarkMode = true
err = router.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark) err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -300,7 +302,7 @@ func (t *Inbound) Tag() string {
func (t *Inbound) Start() error { func (t *Inbound) Start() error {
if C.IsAndroid && t.platformInterface == nil { if C.IsAndroid && t.platformInterface == nil {
t.tunOptions.BuildAndroidRules(t.router.PackageManager()) t.tunOptions.BuildAndroidRules(t.networkManager.PackageManager())
} }
if t.tunOptions.Name == "" { if t.tunOptions.Name == "" {
t.tunOptions.Name = tun.CalculateInterfaceName("") t.tunOptions.Name = tun.CalculateInterfaceName("")
@ -338,7 +340,7 @@ func (t *Inbound) Start() error {
Handler: t, Handler: t,
Logger: t.logger, Logger: t.logger,
ForwarderBindInterface: forwarderBindInterface, ForwarderBindInterface: forwarderBindInterface,
InterfaceFinder: t.router.InterfaceFinder(), InterfaceFinder: t.networkManager.InterfaceFinder(),
IncludeAllNetworks: includeAllNetworks, IncludeAllNetworks: includeAllNetworks,
}) })
if err != nil { if err != nil {

View file

@ -100,7 +100,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL
if !options.SystemInterface && tun.WithGVisor { if !options.SystemInterface && tun.WithGVisor {
wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu) wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu)
} else { } else {
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu, options.GSO) wireTunDevice, err = wireguard.NewSystemDevice(service.FromContext[adapter.NetworkManager](ctx), options.InterfaceName, options.LocalAddress, mtu, options.GSO)
} }
if err != nil { if err != nil {
return nil, E.Cause(err, "create WireGuard device") return nil, E.Cause(err, "create WireGuard device")

View file

@ -33,7 +33,7 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rule, err = R.NewDefaultRule(r.ctx, r, nil, geosite.Compile(items)) rule, err = R.NewDefaultRule(r.ctx, nil, geosite.Compile(items))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -145,13 +145,13 @@ func (r *Router) downloadGeoIPDatabase(savePath string) error {
r.logger.Info("downloading geoip database") r.logger.Info("downloading geoip database")
var detour adapter.Outbound var detour adapter.Outbound
if r.geoIPOptions.DownloadDetour != "" { if r.geoIPOptions.DownloadDetour != "" {
outbound, loaded := r.Outbound(r.geoIPOptions.DownloadDetour) outbound, loaded := r.outboundManager.Outbound(r.geoIPOptions.DownloadDetour)
if !loaded { if !loaded {
return E.New("detour outbound not found: ", r.geoIPOptions.DownloadDetour) return E.New("detour outbound not found: ", r.geoIPOptions.DownloadDetour)
} }
detour = outbound detour = outbound
} else { } else {
detour = r.defaultOutboundForConnection detour = r.outboundManager.Default()
} }
if parentDir := filepath.Dir(savePath); parentDir != "" { if parentDir := filepath.Dir(savePath); parentDir != "" {
@ -200,13 +200,13 @@ func (r *Router) downloadGeositeDatabase(savePath string) error {
r.logger.Info("downloading geosite database") r.logger.Info("downloading geosite database")
var detour adapter.Outbound var detour adapter.Outbound
if r.geositeOptions.DownloadDetour != "" { if r.geositeOptions.DownloadDetour != "" {
outbound, loaded := r.Outbound(r.geositeOptions.DownloadDetour) outbound, loaded := r.outboundManager.Outbound(r.geositeOptions.DownloadDetour)
if !loaded { if !loaded {
return E.New("detour outbound not found: ", r.geositeOptions.DownloadDetour) return E.New("detour outbound not found: ", r.geositeOptions.DownloadDetour)
} }
detour = outbound detour = outbound
} else { } else {
detour = r.defaultOutboundForConnection detour = r.outboundManager.Default()
} }
if parentDir := filepath.Dir(savePath); parentDir != "" { if parentDir := filepath.Dir(savePath); parentDir != "" {

337
route/network.go Normal file
View file

@ -0,0 +1,337 @@
package route
import (
"context"
"errors"
"net/netip"
"os"
"runtime"
"syscall"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/common/taskmonitor"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
"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"
)
var _ adapter.NetworkManager = (*NetworkManager)(nil)
type NetworkManager struct {
logger logger.ContextLogger
interfaceFinder *control.DefaultInterfaceFinder
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
}
func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) {
nm := &NetworkManager{
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),
}
usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil && nm.platformInterface.UsePlatformDefaultInterfaceMonitor()
enforceInterfaceMonitor := routeOptions.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(func() {
_ = nm.interfaceFinder.Update()
})
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(nm.networkMonitor, logger, tun.DefaultInterfaceMonitorOptions{
InterfaceFinder: nm.interfaceFinder,
OverrideAndroidVPN: routeOptions.OverrideAndroidVPN,
UnderNetworkExtension: nm.platformInterface != nil && nm.platformInterface.UnderNetworkExtension(),
})
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(nm.notifyNetworkUpdate)
nm.interfaceMonitor = interfaceMonitor
}
} else {
interfaceMonitor := nm.platformInterface.CreateDefaultInterfaceMonitor(logger)
interfaceMonitor.RegisterCallback(nm.notifyNetworkUpdate)
nm.interfaceMonitor = interfaceMonitor
}
return nm, nil
}
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()
monitor.Finish()
if err != nil {
return err
}
}
if r.networkMonitor != nil {
monitor.Start("initialize network monitor")
err := r.networkMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
case adapter.StartStateStart:
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")
}
}
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
}
}
case adapter.StartStatePostStart:
r.started = true
}
return nil
}
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 {
return E.Cause(err, "close package manager")
})
monitor.Finish()
}
if r.powerListener != nil {
monitor.Start("close power listener")
err = E.Append(err, r.powerListener.Close(), func(err error) error {
return E.Cause(err, "close power listener")
})
monitor.Finish()
}
return nil
}
func (r *NetworkManager) InterfaceFinder() control.InterfaceFinder {
return r.interfaceFinder
}
func (r *NetworkManager) UpdateInterfaces() error {
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
return r.interfaceFinder.Update()
} else {
interfaces, err := r.platformInterface.Interfaces()
if err != nil {
return err
}
r.interfaceFinder.UpdateInterfaces(interfaces)
return nil
}
}
func (r *NetworkManager) DefaultInterface() string {
return r.defaultInterface
}
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 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
}
}
return
})
}
}
func (r *NetworkManager) DefaultMark() uint32 {
return r.defaultMark
}
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) 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) WIFIState() adapter.WIFIState {
return r.wifiState
}
func (r *NetworkManager) ResetNetwork() {
conntrack.Close()
for _, outbound := range r.outboundManager.Outbounds() {
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener {
listener.InterfaceUpdated()
}
}
}
func (r *NetworkManager) notifyNetworkUpdate(event int) {
if event == tun.EventNoRoute {
r.pauseManager.NetworkPause()
r.logger.Error("missing default interface")
} else {
r.pauseManager.NetworkWake()
if C.IsAndroid && r.platformInterface == nil {
var vpnStatus string
if r.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled"
} else {
vpnStatus = "disabled"
}
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
} else {
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
}
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 {
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
}
}
}
}
if !r.started {
return
}
r.ResetNetwork()
}
func (r *NetworkManager) notifyWindowsPowerEvent(event int) {
switch event {
case winpowrprof.EVENT_SUSPEND:
r.pauseManager.DevicePause()
r.ResetNetwork()
case winpowrprof.EVENT_RESUME:
if !r.pauseManager.IsDevicePaused() {
return
}
fallthrough
case winpowrprof.EVENT_RESUME_AUTOMATIC:
r.pauseManager.DeviceWake()
r.ResetNetwork()
}
}
func (r *NetworkManager) OnPackagesUpdated(packages int, sharedUsers int) {
r.logger.Info("updated packages list: ", packages, " packages, ", sharedUsers, " shared users")
}

View file

@ -58,8 +58,8 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
if metadata.LastInbound == metadata.InboundDetour { if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour) return E.New("routing loop on detour: ", metadata.InboundDetour)
} }
detour := r.inboundByTag[metadata.InboundDetour] detour, loaded := r.inboundManager.Get(metadata.InboundDetour)
if detour == nil { if !loaded {
return E.New("inbound detour not found: ", metadata.InboundDetour) return E.New("inbound detour not found: ", metadata.InboundDetour)
} }
injectable, isInjectable := detour.(adapter.TCPInjectableInbound) injectable, isInjectable := detour.(adapter.TCPInjectableInbound)
@ -100,7 +100,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
if selectedRule != nil { if selectedRule != nil {
switch action := selectedRule.Action().(type) { switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute: case *rule.RuleActionRoute:
selectedOutbound, loaded := r.Outbound(action.Outbound) selectedOutbound, loaded := r.outboundManager.Outbound(action.Outbound)
if !loaded { if !loaded {
buf.ReleaseMulti(buffers) buf.ReleaseMulti(buffers)
return E.New("outbound not found: ", action.Outbound) return E.New("outbound not found: ", action.Outbound)
@ -128,13 +128,14 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
} }
} }
if selectedRule == nil { if selectedRule == nil {
if r.defaultOutboundForConnection == nil { defaultOutbound := r.outboundManager.Default()
if !common.Contains(defaultOutbound.Network(), N.NetworkTCP) {
buf.ReleaseMulti(buffers) buf.ReleaseMulti(buffers)
return E.New("missing default outbound with TCP support") return E.New("TCP is not supported by default outbound: ", defaultOutbound.Tag())
} }
selectedDialer = r.defaultOutboundForConnection selectedDialer = defaultOutbound
selectedTag = r.defaultOutboundForConnection.Tag() selectedTag = defaultOutbound.Tag()
selectedDescription = F.ToString("outbound/", r.defaultOutboundForConnection.Type(), "[", r.defaultOutboundForConnection.Tag(), "]") selectedDescription = F.ToString("outbound/", defaultOutbound.Type(), "[", defaultOutbound.Tag(), "]")
} }
for _, buffer := range buffers { for _, buffer := range buffers {
@ -217,8 +218,8 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
if metadata.LastInbound == metadata.InboundDetour { if metadata.LastInbound == metadata.InboundDetour {
return E.New("routing loop on detour: ", metadata.InboundDetour) return E.New("routing loop on detour: ", metadata.InboundDetour)
} }
detour := r.inboundByTag[metadata.InboundDetour] detour, loaded := r.inboundManager.Get(metadata.InboundDetour)
if detour == nil { if !loaded {
return E.New("inbound detour not found: ", metadata.InboundDetour) return E.New("inbound detour not found: ", metadata.InboundDetour)
} }
injectable, isInjectable := detour.(adapter.UDPInjectableInbound) injectable, isInjectable := detour.(adapter.UDPInjectableInbound)
@ -254,7 +255,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
if selectedRule != nil { if selectedRule != nil {
switch action := selectedRule.Action().(type) { switch action := selectedRule.Action().(type) {
case *rule.RuleActionRoute: case *rule.RuleActionRoute:
selectedOutbound, loaded := r.Outbound(action.Outbound) selectedOutbound, loaded := r.outboundManager.Outbound(action.Outbound)
if !loaded { if !loaded {
N.ReleaseMultiPacketBuffer(packetBuffers) N.ReleaseMultiPacketBuffer(packetBuffers)
return E.New("outbound not found: ", action.Outbound) return E.New("outbound not found: ", action.Outbound)
@ -279,13 +280,14 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
} }
} }
if selectedRule == nil || selectReturn { if selectedRule == nil || selectReturn {
if r.defaultOutboundForPacketConnection == nil { defaultOutbound := r.outboundManager.Default()
if !common.Contains(defaultOutbound.Network(), N.NetworkUDP) {
N.ReleaseMultiPacketBuffer(packetBuffers) N.ReleaseMultiPacketBuffer(packetBuffers)
return E.New("missing default outbound with UDP support") return E.New("UDP is not supported by outbound: ", defaultOutbound.Tag())
} }
selectedDialer = r.defaultOutboundForPacketConnection selectedDialer = defaultOutbound
selectedTag = r.defaultOutboundForPacketConnection.Tag() selectedTag = defaultOutbound.Tag()
selectedDescription = F.ToString("outbound/", r.defaultOutboundForPacketConnection.Type(), "[", r.defaultOutboundForPacketConnection.Tag(), "]") selectedDescription = F.ToString("outbound/", defaultOutbound.Type(), "[", defaultOutbound.Tag(), "]")
} }
for _, buffer := range packetBuffers { for _, buffer := range packetBuffers {
conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination) conn = bufio.NewCachedPacketConn(conn, buffer.Buffer, buffer.Destination)

View file

@ -2,17 +2,14 @@ package route
import ( import (
"context" "context"
"errors"
"net/netip" "net/netip"
"net/url" "net/url"
"os" "os"
"runtime" "runtime"
"strings" "strings"
"syscall"
"time" "time"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geoip"
"github.com/sagernet/sing-box/common/geosite" "github.com/sagernet/sing-box/common/geosite"
@ -25,16 +22,13 @@ import (
R "github.com/sagernet/sing-box/route/rule" R "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-box/transport/fakeip" "github.com/sagernet/sing-box/transport/fakeip"
"github.com/sagernet/sing-dns" "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format" F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/common/ntp"
"github.com/sagernet/sing/common/task" "github.com/sagernet/sing/common/task"
"github.com/sagernet/sing/common/winpowrprof"
"github.com/sagernet/sing/service" "github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/pause" "github.com/sagernet/sing/service/pause"
) )
@ -45,13 +39,10 @@ type Router struct {
ctx context.Context ctx context.Context
logger log.ContextLogger logger log.ContextLogger
dnsLogger log.ContextLogger dnsLogger log.ContextLogger
inboundByTag map[string]adapter.Inbound inboundManager adapter.InboundManager
outbounds []adapter.Outbound outboundManager adapter.OutboundManager
outboundByTag map[string]adapter.Outbound networkManager adapter.NetworkManager
rules []adapter.Rule rules []adapter.Rule
defaultDetour string
defaultOutboundForConnection adapter.Outbound
defaultOutboundForPacketConnection adapter.Outbound
needGeoIPDatabase bool needGeoIPDatabase bool
needGeositeDatabase bool needGeositeDatabase bool
geoIPOptions option.GeoIPOptions geoIPOptions option.GeoIPOptions
@ -71,15 +62,6 @@ type Router struct {
transportDomainStrategy map[dns.Transport]dns.DomainStrategy transportDomainStrategy map[dns.Transport]dns.DomainStrategy
dnsReverseMapping *DNSReverseMapping dnsReverseMapping *DNSReverseMapping
fakeIPStore adapter.FakeIPStore fakeIPStore adapter.FakeIPStore
interfaceFinder *control.DefaultInterfaceFinder
autoDetectInterface bool
defaultInterface string
defaultMark uint32
autoRedirectOutputMark uint32
networkMonitor tun.NetworkUpdateMonitor
interfaceMonitor tun.DefaultInterfaceMonitor
packageManager tun.PackageManager
powerListener winpowrprof.EventListener
processSearcher process.Searcher processSearcher process.Searcher
timeService *ntp.Service timeService *ntp.Service
pauseManager pause.Manager pauseManager pause.Manager
@ -87,24 +69,17 @@ type Router struct {
v2rayServer adapter.V2RayServer v2rayServer adapter.V2RayServer
platformInterface platform.Interface platformInterface platform.Interface
needWIFIState bool needWIFIState bool
enforcePackageManager bool
wifiState adapter.WIFIState
started bool started bool
} }
func NewRouter( func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions, ntpOptions option.NTPOptions) (*Router, error) {
ctx context.Context,
logFactory log.Factory,
options option.RouteOptions,
dnsOptions option.DNSOptions,
ntpOptions option.NTPOptions,
inbounds []option.Inbound,
) (*Router, error) {
router := &Router{ router := &Router{
ctx: ctx, ctx: ctx,
logger: logFactory.NewLogger("router"), logger: logFactory.NewLogger("router"),
dnsLogger: logFactory.NewLogger("dns"), dnsLogger: logFactory.NewLogger("dns"),
outboundByTag: make(map[string]adapter.Outbound), inboundManager: service.FromContext[adapter.InboundManager](ctx),
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
networkManager: service.FromContext[adapter.NetworkManager](ctx),
rules: make([]adapter.Rule, 0, len(options.Rules)), rules: make([]adapter.Rule, 0, len(options.Rules)),
dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)), dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)),
ruleSetMap: make(map[string]adapter.RuleSet), ruleSetMap: make(map[string]adapter.RuleSet),
@ -114,22 +89,12 @@ func NewRouter(
geositeOptions: common.PtrValueOrDefault(options.Geosite), geositeOptions: common.PtrValueOrDefault(options.Geosite),
geositeCache: make(map[string]adapter.Rule), geositeCache: make(map[string]adapter.Rule),
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess, needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
defaultDetour: options.Final,
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy), defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
interfaceFinder: control.NewDefaultInterfaceFinder(),
autoDetectInterface: options.AutoDetectInterface,
defaultInterface: options.DefaultInterface,
defaultMark: options.DefaultMark,
pauseManager: service.FromContext[pause.Manager](ctx), pauseManager: service.FromContext[pause.Manager](ctx),
platformInterface: service.FromContext[platform.Interface](ctx), platformInterface: service.FromContext[platform.Interface](ctx),
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule), needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
enforcePackageManager: common.Any(inbounds, func(inbound option.Inbound) bool {
if tunOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN && tunOptions.AutoRoute {
return true
}
return false
}),
} }
ctx = service.ContextWith[adapter.Router](ctx, router)
router.dnsClient = dns.NewClient(dns.ClientOptions{ router.dnsClient = dns.NewClient(dns.ClientOptions{
DisableCache: dnsOptions.DNSClientOptions.DisableCache, DisableCache: dnsOptions.DNSClientOptions.DisableCache,
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire, DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
@ -147,14 +112,14 @@ func NewRouter(
Logger: router.dnsLogger, Logger: router.dnsLogger,
}) })
for i, ruleOptions := range options.Rules { for i, ruleOptions := range options.Rules {
routeRule, err := R.NewRule(ctx, router, router.logger, ruleOptions, true) routeRule, err := R.NewRule(ctx, router.logger, ruleOptions, true)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse rule[", i, "]") return nil, E.Cause(err, "parse rule[", i, "]")
} }
router.rules = append(router.rules, routeRule) router.rules = append(router.rules, routeRule)
} }
for i, dnsRuleOptions := range dnsOptions.Rules { for i, dnsRuleOptions := range dnsOptions.Rules {
dnsRule, err := R.NewDNSRule(ctx, router, router.logger, dnsRuleOptions, true) dnsRule, err := R.NewDNSRule(ctx, router.logger, dnsRuleOptions, true)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse dns rule[", i, "]") return nil, E.Cause(err, "parse dns rule[", i, "]")
} }
@ -164,7 +129,7 @@ func NewRouter(
if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists { if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists {
return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag) return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag)
} }
ruleSet, err := R.NewRuleSet(ctx, router, router.logger, ruleSetOptions) ruleSet, err := R.NewRuleSet(ctx, router.logger, ruleSetOptions)
if err != nil { if err != nil {
return nil, E.Cause(err, "parse rule-set[", i, "]") return nil, E.Cause(err, "parse rule-set[", i, "]")
} }
@ -191,7 +156,6 @@ func NewRouter(
transportTags[i] = tag transportTags[i] = tag
transportTagMap[tag] = true transportTagMap[tag] = true
} }
ctx = service.ContextWith[adapter.Router](ctx, router)
outboundManager := service.FromContext[adapter.OutboundManager](ctx) outboundManager := service.FromContext[adapter.OutboundManager](ctx)
for { for {
lastLen := len(dummyTransportMap) lastLen := len(dummyTransportMap)
@ -298,7 +262,7 @@ func NewRouter(
Context: ctx, Context: ctx,
Name: "local", Name: "local",
Address: "local", Address: "local",
Dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{})), Dialer: common.Must1(dialer.NewDefault(router.networkManager, option.DialerOptions{})),
}))) })))
} }
defaultTransport = transports[0] defaultTransport = transports[0]
@ -327,44 +291,6 @@ func NewRouter(
router.fakeIPStore = fakeip.NewStore(ctx, router.logger, inet4Range, inet6Range) router.fakeIPStore = fakeip.NewStore(ctx, router.logger, inet4Range, inet6Range)
} }
usePlatformDefaultInterfaceMonitor := router.platformInterface != nil && router.platformInterface.UsePlatformDefaultInterfaceMonitor()
enforceInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
if httpMixedOptions, isHTTPMixed := inbound.Options.(*option.HTTPMixedInboundOptions); isHTTPMixed && httpMixedOptions.SetSystemProxy {
return true
}
if tunOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN && tunOptions.AutoRoute {
return true
}
return false
})
if !usePlatformDefaultInterfaceMonitor {
networkMonitor, err := tun.NewNetworkUpdateMonitor(router.logger)
if !((err != nil && !enforceInterfaceMonitor) || errors.Is(err, os.ErrInvalid)) {
if err != nil {
return nil, err
}
router.networkMonitor = networkMonitor
networkMonitor.RegisterCallback(func() {
_ = router.interfaceFinder.Update()
})
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, router.logger, tun.DefaultInterfaceMonitorOptions{
InterfaceFinder: router.interfaceFinder,
OverrideAndroidVPN: options.OverrideAndroidVPN,
UnderNetworkExtension: router.platformInterface != nil && router.platformInterface.UnderNetworkExtension(),
})
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
router.interfaceMonitor = interfaceMonitor
}
} else {
interfaceMonitor := router.platformInterface.CreateDefaultInterfaceMonitor(router.logger)
interfaceMonitor.RegisterCallback(router.notifyNetworkUpdate)
router.interfaceMonitor = interfaceMonitor
}
if ntpOptions.Enabled { if ntpOptions.Enabled {
ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions) ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions)
if err != nil { if err != nil {
@ -384,33 +310,10 @@ func NewRouter(
return router, nil return router, nil
} }
func (r *Router) Outbounds() []adapter.Outbound {
if !r.started {
return nil
}
return r.outbounds
}
func (r *Router) Start(stage adapter.StartStage) error { func (r *Router) Start(stage adapter.StartStage) error {
monitor := taskmonitor.New(r.logger, C.StartTimeout) monitor := taskmonitor.New(r.logger, C.StartTimeout)
switch stage { switch stage {
case adapter.StartStateInitialize: case adapter.StartStateInitialize:
if r.interfaceMonitor != nil {
monitor.Start("initialize interface monitor")
err := r.interfaceMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
if r.networkMonitor != nil {
monitor.Start("initialize network monitor")
err := r.networkMonitor.Start()
monitor.Finish()
if err != nil {
return err
}
}
if r.fakeIPStore != nil { if r.fakeIPStore != nil {
monitor.Start("initialize fakeip store") monitor.Start("initialize fakeip store")
err := r.fakeIPStore.Start() err := r.fakeIPStore.Start()
@ -457,49 +360,10 @@ func (r *Router) Start(stage adapter.StartStage) error {
r.geositeReader = nil r.geositeReader = nil
} }
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")
}
}
monitor.Start("initialize DNS client") monitor.Start("initialize DNS client")
r.dnsClient.Start() r.dnsClient.Start()
monitor.Finish() monitor.Finish()
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")
}
if r.enforcePackageManager {
monitor.Start("start package manager")
err = packageManager.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "start package manager")
}
}
r.packageManager = packageManager
}
for i, rule := range r.dnsRules { for i, rule := range r.dnsRules {
monitor.Start("initialize DNS rule[", i, "]") monitor.Start("initialize DNS rule[", i, "]")
err := rule.Start() err := rule.Start()
@ -552,26 +416,13 @@ func (r *Router) Start(stage adapter.StartStage) error {
cacheContext.Close() cacheContext.Close()
} }
needFindProcess := r.needFindProcess needFindProcess := r.needFindProcess
needWIFIState := r.needWIFIState
for _, ruleSet := range r.ruleSets { for _, ruleSet := range r.ruleSets {
metadata := ruleSet.Metadata() metadata := ruleSet.Metadata()
if metadata.ContainsProcessRule { if metadata.ContainsProcessRule {
needFindProcess = true needFindProcess = true
} }
if metadata.ContainsWIFIRule { if metadata.ContainsWIFIRule {
needWIFIState = true r.needWIFIState = true
}
}
if C.IsAndroid && r.platformInterface == nil && !r.enforcePackageManager {
if needFindProcess {
monitor.Start("start package manager")
err := r.packageManager.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "start package manager")
}
} else {
r.packageManager = nil
} }
} }
if needFindProcess { if needFindProcess {
@ -581,7 +432,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
monitor.Start("initialize process searcher") monitor.Start("initialize process searcher")
searcher, err := process.NewSearcher(process.Config{ searcher, err := process.NewSearcher(process.Config{
Logger: r.logger, Logger: r.logger,
PackageManager: r.packageManager, PackageManager: r.networkManager.PackageManager(),
}) })
monitor.Finish() monitor.Finish()
if err != nil { if err != nil {
@ -593,15 +444,6 @@ func (r *Router) Start(stage adapter.StartStage) error {
} }
} }
} }
if needWIFIState && r.platformInterface != nil {
monitor.Start("initialize WIFI state")
r.needWIFIState = true
r.interfaceMonitor.RegisterCallback(func(_ int) {
r.updateWIFIState()
})
r.updateWIFIState()
monitor.Finish()
}
for i, rule := range r.rules { for i, rule := range r.rules {
monitor.Start("initialize rule[", i, "]") monitor.Start("initialize rule[", i, "]")
err := rule.Start() err := rule.Start()
@ -660,34 +502,6 @@ func (r *Router) Close() error {
}) })
monitor.Finish() 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()
}
if r.packageManager != nil {
monitor.Start("close package manager")
err = E.Append(err, r.packageManager.Close(), func(err error) error {
return E.Cause(err, "close package manager")
})
monitor.Finish()
}
if r.powerListener != nil {
monitor.Start("close power listener")
err = E.Append(err, r.powerListener.Close(), func(err error) error {
return E.Cause(err, "close power listener")
})
monitor.Finish()
}
if r.timeService != nil { if r.timeService != nil {
monitor.Start("close time service") monitor.Start("close time service")
err = E.Append(err, r.timeService.Close(), func(err error) error { err = E.Append(err, r.timeService.Close(), func(err error) error {
@ -705,25 +519,6 @@ func (r *Router) Close() error {
return err return err
} }
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
outbound, loaded := r.outboundByTag[tag]
return outbound, loaded
}
func (r *Router) DefaultOutbound(network string) (adapter.Outbound, error) {
if network == N.NetworkTCP {
if r.defaultOutboundForConnection == nil {
return nil, E.New("missing default outbound for TCP connections")
}
return r.defaultOutboundForConnection, nil
} else {
if r.defaultOutboundForPacketConnection == nil {
return nil, E.New("missing default outbound for UDP connections")
}
return r.defaultOutboundForPacketConnection, nil
}
}
func (r *Router) FakeIPStore() adapter.FakeIPStore { func (r *Router) FakeIPStore() adapter.FakeIPStore {
return r.fakeIPStore return r.fakeIPStore
} }
@ -737,96 +532,10 @@ func (r *Router) NeedWIFIState() bool {
return r.needWIFIState return r.needWIFIState
} }
func (r *Router) InterfaceFinder() control.InterfaceFinder {
return r.interfaceFinder
}
func (r *Router) UpdateInterfaces() error {
if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
return r.interfaceFinder.Update()
} else {
interfaces, err := r.platformInterface.Interfaces()
if err != nil {
return err
}
r.interfaceFinder.UpdateInterfaces(interfaces)
return nil
}
}
func (r *Router) AutoDetectInterface() bool {
return r.autoDetectInterface
}
func (r *Router) 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 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
}
}
return
})
}
}
func (r *Router) 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 *Router) AutoRedirectOutputMark() uint32 {
return r.autoRedirectOutputMark
}
func (r *Router) DefaultInterface() string {
return r.defaultInterface
}
func (r *Router) DefaultMark() uint32 {
return r.defaultMark
}
func (r *Router) Rules() []adapter.Rule { func (r *Router) Rules() []adapter.Rule {
return r.rules return r.rules
} }
func (r *Router) WIFIState() adapter.WIFIState {
return r.wifiState
}
func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor {
return r.networkMonitor
}
func (r *Router) InterfaceMonitor() tun.DefaultInterfaceMonitor {
return r.interfaceMonitor
}
func (r *Router) PackageManager() tun.PackageManager {
return r.packageManager
}
func (r *Router) ClashServer() adapter.ClashServer { func (r *Router) ClashServer() adapter.ClashServer {
return r.clashServer return r.clashServer
} }
@ -843,10 +552,6 @@ func (r *Router) SetV2RayServer(server adapter.V2RayServer) {
r.v2rayServer = server r.v2rayServer = server
} }
func (r *Router) OnPackagesUpdated(packages int, sharedUsers int) {
r.logger.Info("updated packages list: ", packages, " packages, ", sharedUsers, " shared users")
}
func (r *Router) NewError(ctx context.Context, err error) { func (r *Router) NewError(ctx context.Context, err error) {
common.Close(err) common.Close(err)
if E.IsClosedOrCanceled(err) { if E.IsClosedOrCanceled(err) {
@ -856,75 +561,9 @@ func (r *Router) NewError(ctx context.Context, err error) {
r.logger.ErrorContext(ctx, err) r.logger.ErrorContext(ctx, err)
} }
func (r *Router) notifyNetworkUpdate(event int) { func (r *Router) ResetNetwork() {
if event == tun.EventNoRoute { r.networkManager.ResetNetwork()
r.pauseManager.NetworkPause()
r.logger.Error("missing default interface")
} else {
r.pauseManager.NetworkWake()
if C.IsAndroid && r.platformInterface == nil {
var vpnStatus string
if r.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled"
} else {
vpnStatus = "disabled"
}
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
} else {
r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
}
}
if !r.started {
return
}
_ = r.ResetNetwork()
}
func (r *Router) ResetNetwork() error {
conntrack.Close()
for _, outbound := range r.outbounds {
listener, isListener := outbound.(adapter.InterfaceUpdateListener)
if isListener {
listener.InterfaceUpdated()
}
}
for _, transport := range r.transports { for _, transport := range r.transports {
transport.Reset() transport.Reset()
} }
return nil
}
func (r *Router) updateWIFIState() {
if r.platformInterface == nil {
return
}
state := r.platformInterface.ReadWIFIState()
if state != r.wifiState {
r.wifiState = state
if state.SSID == "" && state.BSSID == "" {
r.logger.Info("updated WIFI state: disconnected")
} else {
r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
}
}
}
func (r *Router) notifyWindowsPowerEvent(event int) {
switch event {
case winpowrprof.EVENT_SUSPEND:
r.pauseManager.DevicePause()
_ = r.ResetNetwork()
case winpowrprof.EVENT_RESUME:
if !r.pauseManager.IsDevicePaused() {
return
}
fallthrough
case winpowrprof.EVENT_RESUME_AUTOMATIC:
r.pauseManager.DeviceWake()
_ = r.ResetNetwork()
}
} }

View file

@ -9,9 +9,10 @@ import (
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service"
) )
func NewRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Rule, checkOutbound bool) (adapter.Rule, error) { func NewRule(ctx context.Context, logger log.ContextLogger, options option.Rule, checkOutbound bool) (adapter.Rule, error) {
switch options.Type { switch options.Type {
case "", C.RuleTypeDefault: case "", C.RuleTypeDefault:
if !options.DefaultOptions.IsValid() { if !options.DefaultOptions.IsValid() {
@ -23,7 +24,7 @@ func NewRule(ctx context.Context, router adapter.Router, logger log.ContextLogge
return nil, E.New("missing outbound field") return nil, E.New("missing outbound field")
} }
} }
return NewDefaultRule(ctx, router, logger, options.DefaultOptions) return NewDefaultRule(ctx, logger, options.DefaultOptions)
case C.RuleTypeLogical: case C.RuleTypeLogical:
if !options.LogicalOptions.IsValid() { if !options.LogicalOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
@ -34,7 +35,7 @@ func NewRule(ctx context.Context, router adapter.Router, logger log.ContextLogge
return nil, E.New("missing outbound field") return nil, E.New("missing outbound field")
} }
} }
return NewLogicalRule(ctx, router, logger, options.LogicalOptions) return NewLogicalRule(ctx, logger, options.LogicalOptions)
default: default:
return nil, E.New("unknown rule type: ", options.Type) return nil, E.New("unknown rule type: ", options.Type)
} }
@ -51,7 +52,7 @@ type RuleItem interface {
String() string String() string
} }
func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) { func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) {
action, err := NewRuleAction(ctx, logger, options.RuleAction) action, err := NewRuleAction(ctx, logger, options.RuleAction)
if err != nil { if err != nil {
return nil, E.Cause(err, "action") return nil, E.Cause(err, "action")
@ -62,6 +63,8 @@ func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.Conte
action: action, action: action,
}, },
} }
router := service.FromContext[adapter.Router](ctx)
networkManager := service.FromContext[adapter.NetworkManager](ctx)
if len(options.Inbound) > 0 { if len(options.Inbound) > 0 {
item := NewInboundRule(options.Inbound) item := NewInboundRule(options.Inbound)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
@ -221,12 +224,12 @@ func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.Conte
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFISSID) > 0 { if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(router, options.WIFISSID) item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFIBSSID) > 0 { if len(options.WIFIBSSID) > 0 {
item := NewWIFIBSSIDItem(router, options.WIFIBSSID) item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
@ -253,7 +256,7 @@ type LogicalRule struct {
abstractLogicalRule abstractLogicalRule
} }
func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) { func NewLogicalRule(ctx context.Context, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) {
action, err := NewRuleAction(ctx, logger, options.RuleAction) action, err := NewRuleAction(ctx, logger, options.RuleAction)
if err != nil { if err != nil {
return nil, E.Cause(err, "action") return nil, E.Cause(err, "action")
@ -274,7 +277,7 @@ func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.Conte
return nil, E.New("unknown logical mode: ", options.Mode) return nil, E.New("unknown logical mode: ", options.Mode)
} }
for i, subOptions := range options.Rules { for i, subOptions := range options.Rules {
subRule, err := NewRule(ctx, router, logger, subOptions, false) subRule, err := NewRule(ctx, logger, subOptions, false)
if err != nil { if err != nil {
return nil, E.Cause(err, "sub rule[", i, "]") return nil, E.Cause(err, "sub rule[", i, "]")
} }

View file

@ -10,9 +10,10 @@ import (
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service"
) )
func NewDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DNSRule, checkServer bool) (adapter.DNSRule, error) { func NewDNSRule(ctx context.Context, logger log.ContextLogger, options option.DNSRule, checkServer bool) (adapter.DNSRule, error) {
switch options.Type { switch options.Type {
case "", C.RuleTypeDefault: case "", C.RuleTypeDefault:
if !options.DefaultOptions.IsValid() { if !options.DefaultOptions.IsValid() {
@ -24,7 +25,7 @@ func NewDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLo
return nil, E.New("missing server field") return nil, E.New("missing server field")
} }
} }
return NewDefaultDNSRule(ctx, router, logger, options.DefaultOptions) return NewDefaultDNSRule(ctx, logger, options.DefaultOptions)
case C.RuleTypeLogical: case C.RuleTypeLogical:
if !options.LogicalOptions.IsValid() { if !options.LogicalOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
@ -35,7 +36,7 @@ func NewDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLo
return nil, E.New("missing server field") return nil, E.New("missing server field")
} }
} }
return NewLogicalDNSRule(ctx, router, logger, options.LogicalOptions) return NewLogicalDNSRule(ctx, logger, options.LogicalOptions)
default: default:
return nil, E.New("unknown rule type: ", options.Type) return nil, E.New("unknown rule type: ", options.Type)
} }
@ -47,7 +48,7 @@ type DefaultDNSRule struct {
abstractDefaultRule abstractDefaultRule
} }
func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) { func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
rule := &DefaultDNSRule{ rule := &DefaultDNSRule{
abstractDefaultRule: abstractDefaultRule{ abstractDefaultRule: abstractDefaultRule{
invert: options.Invert, invert: options.Invert,
@ -59,6 +60,8 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
router := service.FromContext[adapter.Router](ctx)
networkManager := service.FromContext[adapter.NetworkManager](ctx)
if options.IPVersion > 0 { if options.IPVersion > 0 {
switch options.IPVersion { switch options.IPVersion {
case 4, 6: case 4, 6:
@ -218,12 +221,12 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFISSID) > 0 { if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(router, options.WIFISSID) item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFIBSSID) > 0 { if len(options.WIFIBSSID) > 0 {
item := NewWIFIBSSIDItem(router, options.WIFIBSSID) item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
@ -282,7 +285,7 @@ type LogicalDNSRule struct {
abstractLogicalRule abstractLogicalRule
} }
func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) { func NewLogicalDNSRule(ctx context.Context, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
r := &LogicalDNSRule{ r := &LogicalDNSRule{
abstractLogicalRule: abstractLogicalRule{ abstractLogicalRule: abstractLogicalRule{
rules: make([]adapter.HeadlessRule, len(options.Rules)), rules: make([]adapter.HeadlessRule, len(options.Rules)),
@ -299,7 +302,7 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
return nil, E.New("unknown logical mode: ", options.Mode) return nil, E.New("unknown logical mode: ", options.Mode)
} }
for i, subRule := range options.Rules { for i, subRule := range options.Rules {
rule, err := NewDNSRule(ctx, router, logger, subRule, false) rule, err := NewDNSRule(ctx, logger, subRule, false)
if err != nil { if err != nil {
return nil, E.Cause(err, "sub rule[", i, "]") return nil, E.Cause(err, "sub rule[", i, "]")
} }

View file

@ -1,24 +1,27 @@
package rule package rule
import ( import (
"context"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service"
) )
func NewHeadlessRule(router adapter.Router, options option.HeadlessRule) (adapter.HeadlessRule, error) { func NewHeadlessRule(ctx context.Context, options option.HeadlessRule) (adapter.HeadlessRule, error) {
switch options.Type { switch options.Type {
case "", C.RuleTypeDefault: case "", C.RuleTypeDefault:
if !options.DefaultOptions.IsValid() { if !options.DefaultOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
} }
return NewDefaultHeadlessRule(router, options.DefaultOptions) return NewDefaultHeadlessRule(ctx, options.DefaultOptions)
case C.RuleTypeLogical: case C.RuleTypeLogical:
if !options.LogicalOptions.IsValid() { if !options.LogicalOptions.IsValid() {
return nil, E.New("missing conditions") return nil, E.New("missing conditions")
} }
return NewLogicalHeadlessRule(router, options.LogicalOptions) return NewLogicalHeadlessRule(ctx, options.LogicalOptions)
default: default:
return nil, E.New("unknown rule type: ", options.Type) return nil, E.New("unknown rule type: ", options.Type)
} }
@ -30,7 +33,8 @@ type DefaultHeadlessRule struct {
abstractDefaultRule abstractDefaultRule
} }
func NewDefaultHeadlessRule(router adapter.Router, options option.DefaultHeadlessRule) (*DefaultHeadlessRule, error) { func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessRule) (*DefaultHeadlessRule, error) {
networkManager := service.FromContext[adapter.NetworkManager](ctx)
rule := &DefaultHeadlessRule{ rule := &DefaultHeadlessRule{
abstractDefaultRule{ abstractDefaultRule{
invert: options.Invert, invert: options.Invert,
@ -137,15 +141,15 @@ func NewDefaultHeadlessRule(router adapter.Router, options option.DefaultHeadles
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
if len(options.WIFISSID) > 0 { if len(options.WIFISSID) > 0 {
if router != nil { if networkManager != nil {
item := NewWIFISSIDItem(router, options.WIFISSID) item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
} }
if len(options.WIFIBSSID) > 0 { if len(options.WIFIBSSID) > 0 {
if router != nil { if networkManager != nil {
item := NewWIFIBSSIDItem(router, options.WIFIBSSID) item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
rule.items = append(rule.items, item) rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item) rule.allItems = append(rule.allItems, item)
} }
@ -168,7 +172,7 @@ type LogicalHeadlessRule struct {
abstractLogicalRule abstractLogicalRule
} }
func NewLogicalHeadlessRule(router adapter.Router, options option.LogicalHeadlessRule) (*LogicalHeadlessRule, error) { func NewLogicalHeadlessRule(ctx context.Context, options option.LogicalHeadlessRule) (*LogicalHeadlessRule, error) {
r := &LogicalHeadlessRule{ r := &LogicalHeadlessRule{
abstractLogicalRule{ abstractLogicalRule{
rules: make([]adapter.HeadlessRule, len(options.Rules)), rules: make([]adapter.HeadlessRule, len(options.Rules)),
@ -184,7 +188,7 @@ func NewLogicalHeadlessRule(router adapter.Router, options option.LogicalHeadles
return nil, E.New("unknown logical mode: ", options.Mode) return nil, E.New("unknown logical mode: ", options.Mode)
} }
for i, subRule := range options.Rules { for i, subRule := range options.Rules {
rule, err := NewHeadlessRule(router, subRule) rule, err := NewHeadlessRule(ctx, subRule)
if err != nil { if err != nil {
return nil, E.Cause(err, "sub rule[", i, "]") return nil, E.Cause(err, "sub rule[", i, "]")
} }

View file

@ -12,10 +12,10 @@ var _ RuleItem = (*WIFIBSSIDItem)(nil)
type WIFIBSSIDItem struct { type WIFIBSSIDItem struct {
bssidList []string bssidList []string
bssidMap map[string]bool bssidMap map[string]bool
router adapter.Router networkManager adapter.NetworkManager
} }
func NewWIFIBSSIDItem(router adapter.Router, bssidList []string) *WIFIBSSIDItem { func NewWIFIBSSIDItem(networkManager adapter.NetworkManager, bssidList []string) *WIFIBSSIDItem {
bssidMap := make(map[string]bool) bssidMap := make(map[string]bool)
for _, bssid := range bssidList { for _, bssid := range bssidList {
bssidMap[bssid] = true bssidMap[bssid] = true
@ -23,12 +23,12 @@ func NewWIFIBSSIDItem(router adapter.Router, bssidList []string) *WIFIBSSIDItem
return &WIFIBSSIDItem{ return &WIFIBSSIDItem{
bssidList, bssidList,
bssidMap, bssidMap,
router, networkManager,
} }
} }
func (r *WIFIBSSIDItem) Match(metadata *adapter.InboundContext) bool { func (r *WIFIBSSIDItem) Match(metadata *adapter.InboundContext) bool {
return r.bssidMap[r.router.WIFIState().BSSID] return r.bssidMap[r.networkManager.WIFIState().BSSID]
} }
func (r *WIFIBSSIDItem) String() string { func (r *WIFIBSSIDItem) String() string {

View file

@ -12,10 +12,10 @@ var _ RuleItem = (*WIFISSIDItem)(nil)
type WIFISSIDItem struct { type WIFISSIDItem struct {
ssidList []string ssidList []string
ssidMap map[string]bool ssidMap map[string]bool
router adapter.Router networkManager adapter.NetworkManager
} }
func NewWIFISSIDItem(router adapter.Router, ssidList []string) *WIFISSIDItem { func NewWIFISSIDItem(networkManager adapter.NetworkManager, ssidList []string) *WIFISSIDItem {
ssidMap := make(map[string]bool) ssidMap := make(map[string]bool)
for _, ssid := range ssidList { for _, ssid := range ssidList {
ssidMap[ssid] = true ssidMap[ssid] = true
@ -23,12 +23,12 @@ func NewWIFISSIDItem(router adapter.Router, ssidList []string) *WIFISSIDItem {
return &WIFISSIDItem{ return &WIFISSIDItem{
ssidList, ssidList,
ssidMap, ssidMap,
router, networkManager,
} }
} }
func (r *WIFISSIDItem) Match(metadata *adapter.InboundContext) bool { func (r *WIFISSIDItem) Match(metadata *adapter.InboundContext) bool {
return r.ssidMap[r.router.WIFIState().SSID] return r.ssidMap[r.networkManager.WIFIState().SSID]
} }
func (r *WIFISSIDItem) String() string { func (r *WIFISSIDItem) String() string {

View file

@ -13,12 +13,12 @@ import (
"go4.org/netipx" "go4.org/netipx"
) )
func NewRuleSet(ctx context.Context, router adapter.Router, logger logger.ContextLogger, options option.RuleSet) (adapter.RuleSet, error) { func NewRuleSet(ctx context.Context, logger logger.ContextLogger, options option.RuleSet) (adapter.RuleSet, error) {
switch options.Type { switch options.Type {
case C.RuleSetTypeInline, C.RuleSetTypeLocal, "": case C.RuleSetTypeInline, C.RuleSetTypeLocal, "":
return NewLocalRuleSet(ctx, router, logger, options) return NewLocalRuleSet(ctx, logger, options)
case C.RuleSetTypeRemote: case C.RuleSetTypeRemote:
return NewRemoteRuleSet(ctx, router, logger, options), nil return NewRemoteRuleSet(ctx, logger, options), nil
default: default:
return nil, E.New("unknown rule-set type: ", options.Type) return nil, E.New("unknown rule-set type: ", options.Type)
} }

View file

@ -26,7 +26,7 @@ import (
var _ adapter.RuleSet = (*LocalRuleSet)(nil) var _ adapter.RuleSet = (*LocalRuleSet)(nil)
type LocalRuleSet struct { type LocalRuleSet struct {
router adapter.Router ctx context.Context
logger logger.Logger logger logger.Logger
tag string tag string
rules []adapter.HeadlessRule rules []adapter.HeadlessRule
@ -36,9 +36,9 @@ type LocalRuleSet struct {
refs atomic.Int32 refs atomic.Int32
} }
func NewLocalRuleSet(ctx context.Context, router adapter.Router, logger logger.Logger, options option.RuleSet) (*LocalRuleSet, error) { func NewLocalRuleSet(ctx context.Context, logger logger.Logger, options option.RuleSet) (*LocalRuleSet, error) {
ruleSet := &LocalRuleSet{ ruleSet := &LocalRuleSet{
router: router, ctx: ctx,
logger: logger, logger: logger,
tag: options.Tag, tag: options.Tag,
fileFormat: options.Format, fileFormat: options.Format,
@ -130,7 +130,7 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
rules := make([]adapter.HeadlessRule, len(headlessRules)) rules := make([]adapter.HeadlessRule, len(headlessRules))
var err error var err error
for i, ruleOptions := range headlessRules { for i, ruleOptions := range headlessRules {
rules[i], err = NewHeadlessRule(s.router, ruleOptions) rules[i], err = NewHeadlessRule(s.ctx, ruleOptions)
if err != nil { if err != nil {
return E.Cause(err, "parse rule_set.rules.[", i, "]") return E.Cause(err, "parse rule_set.rules.[", i, "]")
} }

View file

@ -35,7 +35,6 @@ var _ adapter.RuleSet = (*RemoteRuleSet)(nil)
type RemoteRuleSet struct { type RemoteRuleSet struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
router adapter.Router
outboundManager adapter.OutboundManager outboundManager adapter.OutboundManager
logger logger.ContextLogger logger logger.ContextLogger
options option.RuleSet options option.RuleSet
@ -53,7 +52,7 @@ type RemoteRuleSet struct {
refs atomic.Int32 refs atomic.Int32
} }
func NewRemoteRuleSet(ctx context.Context, router adapter.Router, logger logger.ContextLogger, options option.RuleSet) *RemoteRuleSet { func NewRemoteRuleSet(ctx context.Context, logger logger.ContextLogger, options option.RuleSet) *RemoteRuleSet {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
var updateInterval time.Duration var updateInterval time.Duration
if options.RemoteOptions.UpdateInterval > 0 { if options.RemoteOptions.UpdateInterval > 0 {
@ -64,7 +63,6 @@ func NewRemoteRuleSet(ctx context.Context, router adapter.Router, logger logger.
return &RemoteRuleSet{ return &RemoteRuleSet{
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
router: router,
outboundManager: service.FromContext[adapter.OutboundManager](ctx), outboundManager: service.FromContext[adapter.OutboundManager](ctx),
logger: logger, logger: logger,
options: options, options: options,
@ -180,7 +178,7 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
} }
rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules)) rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
for i, ruleOptions := range plainRuleSet.Rules { for i, ruleOptions := range plainRuleSet.Rules {
rules[i], err = NewHeadlessRule(s.router, ruleOptions) rules[i], err = NewHeadlessRule(s.ctx, ruleOptions)
if err != nil { if err != nil {
return E.Cause(err, "parse rule_set.rules.[", i, "]") return E.Cause(err, "parse rule_set.rules.[", i, "]")
} }

View file

@ -38,6 +38,7 @@ func init() {
type Transport struct { type Transport struct {
options dns.TransportOptions options dns.TransportOptions
router adapter.Router router adapter.Router
networkManager adapter.NetworkManager
interfaceName string interfaceName string
autoInterface bool autoInterface bool
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback] interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
@ -54,13 +55,9 @@ func NewTransport(options dns.TransportOptions) (*Transport, error) {
if linkURL.Host == "" { if linkURL.Host == "" {
return nil, E.New("missing interface name for DHCP") return nil, E.New("missing interface name for DHCP")
} }
router := service.FromContext[adapter.Router](options.Context)
if router == nil {
return nil, E.New("missing router in context")
}
transport := &Transport{ transport := &Transport{
options: options, options: options,
router: router, networkManager: service.FromContext[adapter.NetworkManager](options.Context),
interfaceName: linkURL.Host, interfaceName: linkURL.Host,
autoInterface: linkURL.Host == "auto", autoInterface: linkURL.Host == "auto",
} }
@ -77,7 +74,7 @@ func (t *Transport) Start() error {
return err return err
} }
if t.autoInterface { if t.autoInterface {
t.interfaceCallback = t.router.InterfaceMonitor().RegisterCallback(t.interfaceUpdated) t.interfaceCallback = t.networkManager.InterfaceMonitor().RegisterCallback(t.interfaceUpdated)
} }
return nil return nil
} }
@ -93,7 +90,7 @@ func (t *Transport) Close() error {
transport.Close() transport.Close()
} }
if t.interfaceCallback != nil { if t.interfaceCallback != nil {
t.router.InterfaceMonitor().UnregisterCallback(t.interfaceCallback) t.networkManager.InterfaceMonitor().UnregisterCallback(t.interfaceCallback)
} }
return nil return nil
} }
@ -125,10 +122,10 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
func (t *Transport) fetchInterface() (*net.Interface, error) { func (t *Transport) fetchInterface() (*net.Interface, error) {
interfaceName := t.interfaceName interfaceName := t.interfaceName
if t.autoInterface { if t.autoInterface {
if t.router.InterfaceMonitor() == nil { if t.networkManager.InterfaceMonitor() == nil {
return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface") return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
} }
interfaceName = t.router.InterfaceMonitor().DefaultInterfaceName(netip.Addr{}) interfaceName = t.networkManager.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
} }
if interfaceName == "" { if interfaceName == "" {
return nil, E.New("missing default interface") return nil, E.New("missing default interface")
@ -177,7 +174,7 @@ func (t *Transport) interfaceUpdated(int) {
func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) error { func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) error {
var listener net.ListenConfig var listener net.ListenConfig
listener.Control = control.Append(listener.Control, control.BindToInterface(t.router.InterfaceFinder(), iface.Name, iface.Index)) listener.Control = control.Append(listener.Control, control.BindToInterface(t.networkManager.InterfaceFinder(), iface.Name, iface.Index))
listener.Control = control.Append(listener.Control, control.ReuseAddr()) listener.Control = control.Append(listener.Control, control.ReuseAddr())
listenAddr := "0.0.0.0:68" listenAddr := "0.0.0.0:68"
if runtime.GOOS == "linux" || runtime.GOOS == "android" { if runtime.GOOS == "linux" || runtime.GOOS == "android" {
@ -255,7 +252,7 @@ func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Ad
return it.String() return it.String()
}), ","), "]") }), ","), "]")
} }
serverDialer := common.Must1(dialer.NewDefault(t.router, option.DialerOptions{ serverDialer := common.Must1(dialer.NewDefault(t.networkManager, option.DialerOptions{
BindInterface: iface.Name, BindInterface: iface.Name,
UDPFragmentDefault: true, UDPFragmentDefault: true,
})) }))

View file

@ -34,7 +34,7 @@ type SystemDevice struct {
closeOnce sync.Once closeOnce sync.Once
} }
func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32, gso bool) (*SystemDevice, error) { func NewSystemDevice(networkManager adapter.NetworkManager, interfaceName string, localPrefixes []netip.Prefix, mtu uint32, gso bool) (*SystemDevice, error) {
var inet4Addresses []netip.Prefix var inet4Addresses []netip.Prefix
var inet6Addresses []netip.Prefix var inet6Addresses []netip.Prefix
for _, prefixes := range localPrefixes { for _, prefixes := range localPrefixes {
@ -49,7 +49,7 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes
} }
return &SystemDevice{ return &SystemDevice{
dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{ dialer: common.Must1(dialer.NewDefault(networkManager, option.DialerOptions{
BindInterface: interfaceName, BindInterface: interfaceName,
})), })),
name: interfaceName, name: interfaceName,