diff --git a/experimental/libbox/config.go b/experimental/libbox/config.go index b27d34ab..7d0627fe 100644 --- a/experimental/libbox/config.go +++ b/experimental/libbox/config.go @@ -66,6 +66,10 @@ func (s *platformInterfaceStub) OpenTun(options *tun.Options, platformOptions op return nil, os.ErrInvalid } +func (s *platformInterfaceStub) UpdateRouteOptions(options *tun.Options, platformInterface option.TunPlatformOptions) error { + return os.ErrInvalid +} + func (s *platformInterfaceStub) UsePlatformDefaultInterfaceMonitor() bool { return true } diff --git a/experimental/libbox/platform.go b/experimental/libbox/platform.go index 9b7423ad..f4bc7ea6 100644 --- a/experimental/libbox/platform.go +++ b/experimental/libbox/platform.go @@ -9,6 +9,7 @@ type PlatformInterface interface { UsePlatformAutoDetectInterfaceControl() bool AutoDetectInterfaceControl(fd int32) error OpenTun(options TunOptions) (int32, error) + UpdateRouteOptions(options TunOptions) error WriteLog(message string) UseProcFS() bool FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error) diff --git a/experimental/libbox/platform/interface.go b/experimental/libbox/platform/interface.go index 23849a3b..1684d88c 100644 --- a/experimental/libbox/platform/interface.go +++ b/experimental/libbox/platform/interface.go @@ -13,6 +13,7 @@ type Interface interface { UsePlatformAutoDetectInterfaceControl() bool AutoDetectInterfaceControl(fd int) error OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) + UpdateRouteOptions(options *tun.Options, platformOptions option.TunPlatformOptions) error CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor Interfaces() ([]adapter.NetworkInterface, error) SetUnderlyingNetworks(networks []adapter.NetworkInterface) error diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 34c3af92..e04c040d 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -148,10 +148,10 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error { func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) { if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 { - return nil, E.New("android: unsupported uid options") + return nil, E.New("platform: unsupported uid options") } if len(options.IncludeAndroidUser) > 0 { - return nil, E.New("android: unsupported android_user option") + return nil, E.New("platform: unsupported android_user option") } routeRanges, err := options.BuildAutoRouteRanges(true) if err != nil { @@ -174,6 +174,20 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions return tun.New(*options) } +func (w *platformInterfaceWrapper) UpdateRouteOptions(options *tun.Options, platformOptions option.TunPlatformOptions) error { + if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 { + return E.New("android: unsupported uid options") + } + if len(options.IncludeAndroidUser) > 0 { + return E.New("android: unsupported android_user option") + } + routeRanges, err := options.BuildAutoRouteRanges(true) + if err != nil { + return err + } + return w.iif.UpdateRouteOptions(&tunOptions{options, routeRanges, platformOptions}) +} + func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor { return &platformDefaultInterfaceMonitor{ platformInterfaceWrapper: w, diff --git a/go.mod b/go.mod index 936e9950..3f7ccc00 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/sagernet/sing-shadowsocks v0.2.7 github.com/sagernet/sing-shadowsocks2 v0.2.0 github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 - github.com/sagernet/sing-tun v0.6.0-beta.6 + github.com/sagernet/sing-tun v0.6.0-beta.6.0.20241223141727-f4579880903f github.com/sagernet/sing-vmess v0.2.0-beta.1 github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/utls v1.6.7 diff --git a/go.sum b/go.sum index 13c1bb1a..ae48d45b 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.2.0-alpha.2 h1:RPrpgAdkP5td0vLfS5ldvYosFjSsZtRPxiyLV6jyKg0= github.com/sagernet/sing-shadowtls v0.2.0-alpha.2/go.mod h1:0j5XlzKxaWRIEjc1uiSKmVoWb0k+L9QgZVb876+thZA= -github.com/sagernet/sing-tun v0.6.0-beta.6 h1:xaIHoH78MqTSvZqQ4SQto8pC1A+X4qXReDRNaC8DQeI= -github.com/sagernet/sing-tun v0.6.0-beta.6/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= +github.com/sagernet/sing-tun v0.6.0-beta.6.0.20241223141727-f4579880903f h1:QT/ZpJ+6nek1DwR8kKHu/DXuS89w4gLHFYyw4zLSZnM= +github.com/sagernet/sing-tun v0.6.0-beta.6.0.20241223141727-f4579880903f/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE= github.com/sagernet/sing-vmess v0.2.0-beta.1 h1:5sXQ23uwNlZuDvygzi0dFtnG0Csm/SNqTjAHXJkpuj4= github.com/sagernet/sing-vmess v0.2.0-beta.1/go.mod h1:fLyE1emIcvQ5DV8reFWnufquZ7MkCSYM5ThodsR9NrQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= diff --git a/protocol/tun/inbound.go b/protocol/tun/inbound.go index 26158d7a..51e7ca78 100644 --- a/protocol/tun/inbound.go +++ b/protocol/tun/inbound.go @@ -209,6 +209,22 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo platformInterface: service.FromContext[platform.Interface](ctx), platformOptions: common.PtrValueOrDefault(options.Platform), } + for _, routeAddressSet := range options.RouteAddressSet { + ruleSet, loaded := router.RuleSet(routeAddressSet) + if !loaded { + return nil, E.New("parse route_address_set: rule-set not found: ", routeAddressSet) + } + ruleSet.IncRef() + inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet) + } + for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet { + ruleSet, loaded := router.RuleSet(routeExcludeAddressSet) + if !loaded { + return nil, E.New("parse route_exclude_address_set: rule-set not found: ", routeExcludeAddressSet) + } + ruleSet.IncRef() + inbound.routeExcludeRuleSet = append(inbound.routeExcludeRuleSet, ruleSet) + } if options.AutoRedirect { if !options.AutoRoute { return nil, E.New("`auto_route` is required by `auto_redirect`") @@ -229,32 +245,11 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo if err != nil { return nil, E.Cause(err, "initialize auto-redirect") } - if runtime.GOOS != "android" { - var markMode bool - for _, routeAddressSet := range options.RouteAddressSet { - ruleSet, loaded := router.RuleSet(routeAddressSet) - if !loaded { - return nil, E.New("parse route_address_set: rule-set not found: ", routeAddressSet) - } - ruleSet.IncRef() - inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet) - markMode = true - } - for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet { - ruleSet, loaded := router.RuleSet(routeExcludeAddressSet) - if !loaded { - return nil, E.New("parse route_exclude_address_set: rule-set not found: ", routeExcludeAddressSet) - } - ruleSet.IncRef() - inbound.routeExcludeRuleSet = append(inbound.routeExcludeRuleSet, ruleSet) - markMode = true - } - if markMode { - inbound.tunOptions.AutoRedirectMarkMode = true - err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark) - if err != nil { - return nil, err - } + if runtime.GOOS != "android" && len(inbound.routeAddressSet) > 0 || len(inbound.routeExcludeAddressSet) > 0 { + inbound.tunOptions.AutoRedirectMarkMode = true + err = networkManager.RegisterAutoRedirectOutputMark(inbound.tunOptions.AutoRedirectOutputMark) + if err != nil { + return nil, err } } } @@ -310,18 +305,62 @@ func (t *Inbound) Start(stage adapter.StartStage) error { if t.tunOptions.Name == "" { t.tunOptions.Name = tun.CalculateInterfaceName("") } + if t.platformInterface == nil || runtime.GOOS != "android" { + t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) + for _, routeRuleSet := range t.routeRuleSet { + ipSets := routeRuleSet.ExtractIPSet() + if len(ipSets) == 0 { + t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name()) + } + t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet)) + routeRuleSet.DecRef() + t.routeAddressSet = append(t.routeAddressSet, ipSets...) + } + t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet) + for _, routeExcludeRuleSet := range t.routeExcludeRuleSet { + ipSets := routeExcludeRuleSet.ExtractIPSet() + if len(ipSets) == 0 { + t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name()) + } + t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet)) + routeExcludeRuleSet.DecRef() + t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...) + } + } var ( tunInterface tun.Tun err error ) monitor := taskmonitor.New(t.logger, C.StartTimeout) - monitor.Start("open tun interface") + tunOptions := t.tunOptions + if t.autoRedirect == nil && !(runtime.GOOS == "android" && t.platformInterface != nil) { + for _, ipSet := range t.routeAddressSet { + for _, prefix := range ipSet.Prefixes() { + if prefix.Addr().Is4() { + tunOptions.Inet4RouteAddress = append(tunOptions.Inet4RouteAddress, prefix) + } else { + tunOptions.Inet6RouteAddress = append(tunOptions.Inet6RouteAddress, prefix) + } + } + } + for _, ipSet := range t.routeExcludeAddressSet { + for _, prefix := range ipSet.Prefixes() { + if prefix.Addr().Is4() { + tunOptions.Inet4RouteExcludeAddress = append(tunOptions.Inet4RouteExcludeAddress, prefix) + } else { + tunOptions.Inet6RouteExcludeAddress = append(tunOptions.Inet6RouteExcludeAddress, prefix) + } + } + } + } + monitor.Start("open interface") if t.platformInterface != nil { - tunInterface, err = t.platformInterface.OpenTun(&t.tunOptions, t.platformOptions) + tunInterface, err = t.platformInterface.OpenTun(&tunOptions, t.platformOptions) } else { - tunInterface, err = tun.New(t.tunOptions) + tunInterface, err = tun.New(tunOptions) } monitor.Finish() + t.tunOptions.Name = tunOptions.Name if err != nil { return E.Cause(err, "configure tun interface") } @@ -366,39 +405,15 @@ func (t *Inbound) Start(stage adapter.StartStage) error { return E.Cause(err, "starting TUN interface") } if t.autoRedirect != nil { - t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) - for _, routeRuleSet := range t.routeRuleSet { - ipSets := routeRuleSet.ExtractIPSet() - if len(ipSets) == 0 { - t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeRuleSet.Name()) - } - t.routeAddressSet = append(t.routeAddressSet, ipSets...) - } - t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet) - for _, routeExcludeRuleSet := range t.routeExcludeRuleSet { - ipSets := routeExcludeRuleSet.ExtractIPSet() - if len(ipSets) == 0 { - t.logger.Warn("route_address_set: no destination IP CIDR rules found in rule-set: ", routeExcludeRuleSet.Name()) - } - t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ipSets...) - } monitor.Start("initialize auto-redirect") err := t.autoRedirect.Start() monitor.Finish() if err != nil { return E.Cause(err, "auto-redirect") } - for _, routeRuleSet := range t.routeRuleSet { - t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet)) - routeRuleSet.DecRef() - } - for _, routeExcludeRuleSet := range t.routeExcludeRuleSet { - t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet)) - routeExcludeRuleSet.DecRef() - } - t.routeAddressSet = nil - t.routeExcludeAddressSet = nil } + t.routeAddressSet = nil + t.routeExcludeAddressSet = nil } return nil } @@ -406,7 +421,41 @@ func (t *Inbound) Start(stage adapter.StartStage) error { func (t *Inbound) updateRouteAddressSet(it adapter.RuleSet) { t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet) t.routeExcludeAddressSet = common.FlatMap(t.routeExcludeRuleSet, adapter.RuleSet.ExtractIPSet) - t.autoRedirect.UpdateRouteAddressSet() + if t.autoRedirect != nil { + t.autoRedirect.UpdateRouteAddressSet() + } else { + tunOptions := t.tunOptions + for _, ipSet := range t.routeAddressSet { + for _, prefix := range ipSet.Prefixes() { + if prefix.Addr().Is4() { + tunOptions.Inet4RouteAddress = append(tunOptions.Inet4RouteAddress, prefix) + } else { + tunOptions.Inet6RouteAddress = append(tunOptions.Inet6RouteAddress, prefix) + } + } + } + for _, ipSet := range t.routeExcludeAddressSet { + for _, prefix := range ipSet.Prefixes() { + if prefix.Addr().Is4() { + tunOptions.Inet4RouteExcludeAddress = append(tunOptions.Inet4RouteExcludeAddress, prefix) + } else { + tunOptions.Inet6RouteExcludeAddress = append(tunOptions.Inet6RouteExcludeAddress, prefix) + } + } + } + if t.platformInterface != nil { + err := t.platformInterface.UpdateRouteOptions(&tunOptions, t.platformOptions) + if err != nil { + t.logger.Error("update route addresses: ", err) + } + } else { + err := t.tunIf.UpdateRouteOptions(tunOptions) + if err != nil { + t.logger.Error("update route addresses: ", err) + } + } + t.logger.Info("updated route addresses") + } t.routeAddressSet = nil t.routeExcludeAddressSet = nil } diff --git a/route/router.go b/route/router.go index 6526778b..642340d4 100644 --- a/route/router.go +++ b/route/router.go @@ -363,7 +363,6 @@ func (r *Router) Start(stage adapter.StartStage) error { return E.Cause(err, "initialize DNS server[", i, "]") } } - case adapter.StartStatePostStart: var cacheContext *adapter.HTTPStartContext if len(r.ruleSets) > 0 { monitor.Start("initialize rule-set") @@ -419,6 +418,7 @@ func (r *Router) Start(stage adapter.StartStage) error { } } } + case adapter.StartStatePostStart: for i, rule := range r.rules { monitor.Start("initialize rule[", i, "]") err := rule.Start()