package limiter import ( "context" "sync" "time" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/protocol/limiter/traffic" CM "github.com/sagernet/sing-box/service/manager/constant" "github.com/sagernet/sing-box/service/node/constant" E "github.com/sagernet/sing/common/exceptions" ) type ManagedTrafficStrategy interface { UpdateStrategies(strategies map[string]traffic.TrafficStrategy) } type TrafficLimiterManager struct { ctx context.Context nodeManager CM.NodeManager logger log.ContextLogger managers map[string]*TrafficLimiterStrategyManager mtx sync.Mutex } func NewTrafficLimiterManager(ctx context.Context, nodeManager CM.NodeManager, logger log.ContextLogger) *TrafficLimiterManager { manager := &TrafficLimiterManager{ ctx: ctx, nodeManager: nodeManager, logger: logger, managers: make(map[string]*TrafficLimiterStrategyManager), } go func() { timer := time.NewTimer(time.Second * 5) for { select { case <-ctx.Done(): return default: } select { case <-timer.C: for _, strategyManager := range manager.managers { strategyManager.mtx.Lock() for _, limiter := range strategyManager.limiters { err := limiter.UpdateRemainingTraffic() if err != nil { logger.ErrorContext(ctx, err) } } strategyManager.mtx.Unlock() } timer.Reset(time.Second * 5) case <-ctx.Done(): return } } }() return manager } func (m *TrafficLimiterManager) AddTrafficLimiterStrategyManager(outbound adapter.Outbound) error { m.mtx.Lock() defer m.mtx.Unlock() limiter, ok := outbound.(*traffic.Outbound) if !ok { return E.New("invalid traffic limiter: ", outbound.Tag()) } strategy, ok := limiter.GetStrategy().(ManagedTrafficStrategy) if !ok { return E.New("strategy ", outbound.Tag(), " is not manager") } m.managers[outbound.Tag()] = &TrafficLimiterStrategyManager{ manager: m, strategy: strategy, strategiesMap: make(map[string]traffic.TrafficStrategy), limiters: make(map[string]*TrafficLimiter), } return nil } func (m *TrafficLimiterManager) GetTrafficLimiterStrategyManager(tag string) (constant.TrafficLimiterStrategyManager, bool) { m.mtx.Lock() defer m.mtx.Unlock() manager, ok := m.managers[tag] return manager, ok } func (m *TrafficLimiterManager) GetTrafficLimiterStrategyManagerTags() []string { m.mtx.Lock() defer m.mtx.Unlock() tags := make([]string, 0, len(m.managers)) for tag := range m.managers { tags = append(tags, tag) } return tags } type TrafficLimiterStrategyManager struct { manager *TrafficLimiterManager strategy ManagedTrafficStrategy strategiesMap map[string]traffic.TrafficStrategy limiters map[string]*TrafficLimiter mtx sync.Mutex } func (i *TrafficLimiterStrategyManager) postUpdate() { i.strategy.UpdateStrategies(i.strategiesMap) } func (i *TrafficLimiterStrategyManager) UpdateTrafficLimiter(limiter CM.TrafficLimiter) { i.mtx.Lock() defer i.mtx.Unlock() trafficLimiter := NewTrafficLimiter(i.manager.nodeManager, limiter) strategy, err := traffic.CreateStrategy(trafficLimiter, limiter.Strategy, limiter.Mode) if err != nil { i.manager.logger.ErrorContext(i.manager.ctx, err) return } i.limiters[limiter.Username] = trafficLimiter i.strategiesMap[limiter.Username] = strategy i.postUpdate() } func (i *TrafficLimiterStrategyManager) UpdateTrafficLimiters(limiters []CM.TrafficLimiter) { i.mtx.Lock() defer i.mtx.Unlock() clear(i.strategiesMap) newStrategiesMap := make(map[string]traffic.TrafficStrategy) for _, limiter := range limiters { trafficLimiter := NewTrafficLimiter(i.manager.nodeManager, limiter) strategy, err := traffic.CreateStrategy(trafficLimiter, limiter.Strategy, limiter.Mode) if err != nil { i.manager.logger.ErrorContext(i.manager.ctx, err) continue } i.limiters[limiter.Username] = trafficLimiter newStrategiesMap[limiter.Username] = strategy } i.strategiesMap = newStrategiesMap i.postUpdate() } func (i *TrafficLimiterStrategyManager) DeleteTrafficLimiter(username string) { i.mtx.Lock() defer i.mtx.Unlock() delete(i.strategiesMap, username) i.postUpdate() } type TrafficLimiter struct { manager CM.NodeManager limiter CM.TrafficLimiter new uint64 mtx sync.Mutex } func NewTrafficLimiter(manager CM.NodeManager, limiter CM.TrafficLimiter) *TrafficLimiter { return &TrafficLimiter{manager: manager, limiter: limiter} } func (l *TrafficLimiter) Can(n uint64) error { l.mtx.Lock() defer l.mtx.Unlock() if l.limiter.RawUsed == l.limiter.RawQuota { return E.New("traffic limit exceeded") } if l.limiter.RawUsed+n > l.limiter.RawQuota { l.new += l.limiter.RawQuota - l.limiter.RawUsed l.limiter.RawUsed = l.limiter.RawQuota return E.New("traffic limit exceeded") } return nil } func (l *TrafficLimiter) Add(n uint64) error { l.mtx.Lock() defer l.mtx.Unlock() if l.limiter.RawUsed == l.limiter.RawQuota { return E.New("traffic limit exceeded") } if l.limiter.RawUsed+n > l.limiter.RawQuota { l.new += l.limiter.RawQuota - l.limiter.RawUsed l.limiter.RawUsed = l.limiter.RawQuota return E.New("traffic limit exceeded") } l.limiter.RawUsed += n l.new += n return nil } func (l *TrafficLimiter) UpdateRemainingTraffic() error { l.mtx.Lock() if l.new == 0 { l.mtx.Unlock() return nil } new := l.new l.new = 0 l.mtx.Unlock() newUsed, err := l.manager.AddTrafficUsage(l.limiter.ID, new) l.mtx.Lock() defer l.mtx.Unlock() if err == nil { l.limiter.RawUsed = newUsed } else { l.new += new } return nil }