From 02537831e1883a449d0c1b1511e9889740d62e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 1 Oct 2026 23:32:10 +0800 Subject: [PATCH] Move auto-redirect and bridge index allocation out of constructors --- protocol/bridge/backend.go | 19 +++++++---- protocol/bridge/backend_darwin.go | 17 ++++------ protocol/bridge/backend_linux.go | 19 +++++------ protocol/bridge/backend_windows.go | 10 ++---- protocol/tun/inbound.go | 51 ++++++++++++++++-------------- 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/protocol/bridge/backend.go b/protocol/bridge/backend.go index 2c48af97..98143050 100644 --- a/protocol/bridge/backend.go +++ b/protocol/bridge/backend.go @@ -50,21 +50,28 @@ type backendBase struct { readGroup sync.WaitGroup } -func (b *backendBase) init(ctx context.Context, logger logger.ContextLogger, networkManager adapter.NetworkManager, tag string, options option.BridgeOutboundOptions) error { - index, err := allocateBridgeIndex() - if err != nil { - return err - } +func (b *backendBase) init(ctx context.Context, logger logger.ContextLogger, networkManager adapter.NetworkManager, tag string, options option.BridgeOutboundOptions) { b.ctx = ctx b.logger = logger b.networkManager = networkManager b.tag = tag - b.index = index b.bridgeName = options.BridgeName if b.bridgeName == "" { b.bridgeName = "bridge" } b.boundInterface = options.Interface +} + +func (b *backendBase) allocateIndex(scope *adapter.Scope) error { + index, err := allocateBridgeIndex() + if err != nil { + return err + } + scope.Add(func() error { + releaseBridgeIndex(index) + return nil + }) + b.index = index b.inet4Port = addressAt(bridgeInet4Base, index) b.inet6Port = addressAt(bridgeInet6Base, index) return nil diff --git a/protocol/bridge/backend_darwin.go b/protocol/bridge/backend_darwin.go index 4da636d7..a5a9c68a 100644 --- a/protocol/bridge/backend_darwin.go +++ b/protocol/bridge/backend_darwin.go @@ -59,12 +59,7 @@ func newBackend(ctx context.Context, logger logger.ContextLogger, networkManager instance := &backendDarwin{ writeBatch: make([]*buf.Buffer, 0, bridgeWriteBatchSize), } - err := instance.init(ctx, logger, networkManager, tag, options) - if err != nil { - return nil, err - } - instance.inet4Local = addressAt(bridgeInet4LocalBase, instance.index) - instance.inet6Local = addressAt(bridgeInet6LocalBase, instance.index) + instance.init(ctx, logger, networkManager, tag, options) platformInterface := service.FromContext[adapter.PlatformInterface](ctx) if platformInterface != nil && platformInterface.UsePlatformBridge() { instance.platform = platformInterface @@ -75,10 +70,12 @@ func newBackend(ctx context.Context, logger logger.ContextLogger, networkManager func (b *backendDarwin) Start(stage adapter.StartStage, scope *adapter.Scope) error { switch stage { case adapter.StartStateInitialize: - scope.Add(func() error { - releaseBridgeIndex(b.index) - return nil - }) + err := b.allocateIndex(scope) + if err != nil { + return err + } + b.inet4Local = addressAt(bridgeInet4LocalBase, b.index) + b.inet6Local = addressAt(bridgeInet6LocalBase, b.index) case adapter.StartStateStart: b.closed = scope.Context().Done() if b.platform != nil { diff --git a/protocol/bridge/backend_linux.go b/protocol/bridge/backend_linux.go index ce6e1176..8bc6a4e3 100644 --- a/protocol/bridge/backend_linux.go +++ b/protocol/bridge/backend_linux.go @@ -42,10 +42,7 @@ type backendLinux struct { func newBackend(ctx context.Context, logger logger.ContextLogger, networkManager adapter.NetworkManager, tag string, options option.BridgeOutboundOptions) (Backend, error) { instance := &backendLinux{} - err := instance.init(ctx, logger, networkManager, tag, options) - if err != nil { - return nil, err - } + instance.init(ctx, logger, networkManager, tag, options) platformInterface := service.FromContext[adapter.PlatformInterface](ctx) if platformInterface != nil && platformInterface.UsePlatformBridge() { instance.platform = platformInterface @@ -56,9 +53,6 @@ func newBackend(ctx context.Context, logger logger.ContextLogger, networkManager } if instance.boundInterface != "" || instance.platform != nil { instance.routeTable = options.IPRoute2TableIndex - if instance.routeTable == 0 { - instance.routeTable = defaultBridgeTableIndexBase + int(instance.index) - } } return instance, nil } @@ -66,10 +60,13 @@ func newBackend(ctx context.Context, logger logger.ContextLogger, networkManager func (b *backendLinux) Start(stage adapter.StartStage, scope *adapter.Scope) error { switch stage { case adapter.StartStateInitialize: - scope.Add(func() error { - releaseBridgeIndex(b.index) - return nil - }) + err := b.allocateIndex(scope) + if err != nil { + return err + } + if b.routeTable == 0 && (b.boundInterface != "" || b.platform != nil) { + b.routeTable = defaultBridgeTableIndexBase + int(b.index) + } case adapter.StartStateStart: b.closed = scope.Context().Done() if b.platform != nil { diff --git a/protocol/bridge/backend_windows.go b/protocol/bridge/backend_windows.go index af877ce1..9709a180 100644 --- a/protocol/bridge/backend_windows.go +++ b/protocol/bridge/backend_windows.go @@ -133,20 +133,14 @@ type backendWindows struct { func newBackend(ctx context.Context, logger logger.ContextLogger, networkManager adapter.NetworkManager, tag string, options option.BridgeOutboundOptions) (Backend, error) { instance := &backendWindows{} - err := instance.init(ctx, logger, networkManager, tag, options) - if err != nil { - return nil, err - } + instance.init(ctx, logger, networkManager, tag, options) return instance, nil } func (b *backendWindows) Start(stage adapter.StartStage, scope *adapter.Scope) error { switch stage { case adapter.StartStateInitialize: - scope.Add(func() error { - releaseBridgeIndex(b.index) - return nil - }) + return b.allocateIndex(scope) case adapter.StartStateStart: return b.start(scope) } diff --git a/protocol/tun/inbound.go b/protocol/tun/inbound.go index 9347a96e..18c8fd0f 100644 --- a/protocol/tun/inbound.go +++ b/protocol/tun/inbound.go @@ -51,6 +51,8 @@ type Inbound struct { tunStack tun.Stack platformInterface adapter.PlatformInterface platformOptions option.TunPlatformOptions + enableAutoRedirect bool + disableNFTables bool autoRedirect tun.AutoRedirect routeRuleSet []adapter.RuleSet routeExcludeRuleSet []adapter.RuleSet @@ -256,22 +258,12 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo if !options.AutoRoute { return nil, E.New("`auto_route` is required by `auto_redirect`") } - disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES")) - inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{ - TunOptions: &inbound.tunOptions, - Context: ctx, - Handler: (*autoRedirectHandler)(inbound), - Logger: logger, - NetworkMonitor: networkManager.NetworkMonitor(), - InterfaceFinder: networkManager.InterfaceFinder(), - TableName: "sing-box", - DisableNFTables: dErr == nil && disableNFTables, - RouteAddressSet: &inbound.routeAddressSet, - RouteExcludeAddressSet: &inbound.routeExcludeAddressSet, - }) - if err != nil { - return nil, E.Cause(err, "initialize auto-redirect") + if !C.IsLinux { + return nil, E.New("`auto_redirect` is only supported on Linux") } + disableNFTables, parseErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES")) + inbound.enableAutoRedirect = true + inbound.disableNFTables = parseErr == nil && disableNFTables if !C.IsAndroid { inbound.tunOptions.AutoRedirectMarkMode = true if options.NetNs == "" { @@ -328,9 +320,6 @@ func (t *Inbound) Tag() string { func (t *Inbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { switch stage { case adapter.StartStateInitialize: - if t.autoRedirect != nil { - scope.Add(t.autoRedirect.Close) - } if t.tunOptions.DNSModeOrDefault() != tun.DNSModeDisabled && len(t.tunOptions.DNSAddress) == 0 { inet4DNSAddress, _ := t.tunOptions.Inet4DNSAddress() inet6DNSAddress, _ := t.tunOptions.Inet6DNSAddress() @@ -374,6 +363,24 @@ func (t *Inbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { t.tunOptions.NetNs = manager.ResolvePath(t.tunOptions.NetNs) } } + var err error + if t.enableAutoRedirect { + t.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{ + TunOptions: &t.tunOptions, + Context: t.ctx, + Handler: (*autoRedirectHandler)(t), + Logger: t.logger, + NetworkMonitor: t.networkManager.NetworkMonitor(), + InterfaceFinder: t.networkManager.InterfaceFinder(), + TableName: "sing-box", + DisableNFTables: t.disableNFTables, + RouteAddressSet: &t.routeAddressSet, + RouteExcludeAddressSet: &t.routeExcludeAddressSet, + }) + if err != nil { + return E.Cause(err, "initialize auto-redirect") + } + } if t.platformInterface == nil || C.IsWindows { for _, routeRuleSet := range t.routeRuleSet { ipSets := routeRuleSet.ExtractIPSet() @@ -406,10 +413,7 @@ func (t *Inbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { } } } - var ( - tunInterface tun.Tun - err error - ) + var tunInterface tun.Tun monitor := taskmonitor.New(t.logger, C.StartTimeout) tunOptions := t.tunOptions if t.autoRedirect == nil && !(runtime.GOOS == "android" && t.platformInterface != nil) { @@ -492,8 +496,9 @@ func (t *Inbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return E.Cause(err, "starting TUN interface") } if t.autoRedirect != nil { + scope.Add(t.autoRedirect.Close) monitor.Start("initialize auto-redirect") - err := t.autoRedirect.Start() + err = t.autoRedirect.Start() monitor.Finish() if err != nil { return E.Cause(err, "auto-redirect")