fix: 设备数限制某些情况误拒绝旧IP链接BUG

This commit is contained in:
wyx2685
2025-05-29 00:23:21 +09:00
parent eb92c4912d
commit fc284b3b9f
7 changed files with 72 additions and 335 deletions

View File

@@ -1,16 +0,0 @@
package limiter
import log "github.com/sirupsen/logrus"
func ClearOnlineIP() error {
log.WithField("Type", "Limiter").
Debug("Clear online ip...")
limitLock.RLock()
for _, l := range limiter {
l.ConnLimiter.ClearOnlineIP()
}
limitLock.RUnlock()
log.WithField("Type", "Limiter").
Debug("Clear online ip done")
return nil
}

View File

@@ -1,165 +0,0 @@
package limiter
import (
"sync"
"time"
)
type ConnLimiter struct {
realtime bool
ipLimit int
connLimit int
count sync.Map // map[string]int
ip sync.Map // map[string]map[string]int
}
func NewConnLimiter(conn int, ip int, realtime bool) *ConnLimiter {
return &ConnLimiter{
realtime: realtime,
connLimit: conn,
ipLimit: ip,
count: sync.Map{},
ip: sync.Map{},
}
}
func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bool) {
if c.connLimit != 0 {
if v, ok := c.count.Load(user); ok {
if v.(int) >= c.connLimit {
// over connection limit
return true
} else if isTcp {
// tcp protocol
// connection count add
c.count.Store(user, v.(int)+1)
}
} else if isTcp {
// tcp protocol
// store connection count
c.count.Store(user, 1)
}
}
if c.ipLimit == 0 {
return false
}
// first user map
ipMap := new(sync.Map)
if c.realtime {
if isTcp {
ipMap.Store(ip, 2)
} else {
ipMap.Store(ip, 1)
}
} else {
ipMap.Store(ip, time.Now())
}
// check user online ip
if v, ok := c.ip.LoadOrStore(user, ipMap); ok {
// have user
ips := v.(*sync.Map)
cn := 0
if online, ok := ips.Load(ip); ok {
// online ip
if c.realtime {
if isTcp {
// tcp count add
ips.Store(ip, online.(int)+2)
}
} else {
// update connect time for not realtime
ips.Store(ip, time.Now())
}
} else {
// not online ip
ips.Range(func(_, _ interface{}) bool {
cn++
if cn >= c.ipLimit {
limit = true
return false
}
return true
})
if limit {
// over ip limit
return
}
if c.realtime {
if isTcp {
ips.Store(ip, 2)
} else {
ips.Store(ip, 1)
}
} else {
ips.Store(ip, time.Now())
}
}
}
return
}
// DelConnCount Delete tcp connection count, no tcp do not use
func (c *ConnLimiter) DelConnCount(user string, ip string) {
if !c.realtime {
return
}
if c.connLimit != 0 {
if v, ok := c.count.Load(user); ok {
if v.(int) == 1 {
c.count.Delete(user)
} else {
c.count.Store(user, v.(int)-1)
}
}
}
if c.ipLimit == 0 {
return
}
if i, ok := c.ip.Load(user); ok {
is := i.(*sync.Map)
if i, ok := is.Load(ip); ok {
if i.(int) == 2 {
is.Delete(ip)
} else {
is.Store(user, i.(int)-2)
}
notDel := false
c.ip.Range(func(_, _ any) bool {
notDel = true
return false
})
if !notDel {
c.ip.Delete(user)
}
}
}
}
// ClearOnlineIP Clear udp,icmp and other packet protocol online ip
func (c *ConnLimiter) ClearOnlineIP() {
c.ip.Range(func(u, v any) bool {
userIp := v.(*sync.Map)
notDel := false
userIp.Range(func(ip, v any) bool {
notDel = true
if _, ok := v.(int); ok {
if v.(int) == 1 {
// clear packet ip for realtime
userIp.Delete(ip)
}
return true
} else {
// clear ip for not realtime
if v.(time.Time).Before(time.Now().Add(time.Minute)) {
// 1 minute no active
userIp.Delete(ip)
}
}
return true
})
if !notDel {
c.ip.Delete(u)
}
return true
})
}

View File

@@ -1,56 +0,0 @@
package limiter
import (
"sync"
"testing"
"time"
)
var c *ConnLimiter
func init() {
c = NewConnLimiter(1, 1, true)
}
func TestConnLimiter_AddConnCount(t *testing.T) {
t.Log(c.AddConnCount("1", "1", true))
t.Log(c.AddConnCount("1", "2", true))
}
func TestConnLimiter_DelConnCount(t *testing.T) {
t.Log(c.AddConnCount("1", "1", true))
t.Log(c.AddConnCount("1", "2", true))
c.DelConnCount("1", "1")
t.Log(c.AddConnCount("1", "2", true))
}
func TestConnLimiter_ClearOnlineIP(t *testing.T) {
t.Log(c.AddConnCount("1", "1", false))
t.Log(c.AddConnCount("1", "2", false))
c.ClearOnlineIP()
t.Log(c.AddConnCount("1", "2", true))
c.DelConnCount("1", "2")
t.Log(c.AddConnCount("1", "1", false))
// not realtime
c.realtime = false
t.Log(c.AddConnCount("3", "2", true))
c.ClearOnlineIP()
t.Log(c.ip.Load("3"))
time.Sleep(time.Minute)
c.ClearOnlineIP()
t.Log(c.ip.Load("3"))
}
func BenchmarkConnLimiter(b *testing.B) {
wg := sync.WaitGroup{}
for i := 0; i < b.N; i++ {
wg.Add(1)
go func() {
c.AddConnCount("1", "2", true)
c.DelConnCount("1", "2")
wg.Done()
}()
}
wg.Wait()
}

View File

@@ -11,8 +11,6 @@ import (
"github.com/InazumaV/V2bX/common/format"
"github.com/InazumaV/V2bX/conf"
"github.com/juju/ratelimit"
log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/common/task"
)
var limitLock sync.RWMutex
@@ -20,16 +18,6 @@ var limiter map[string]*Limiter
func Init() {
limiter = map[string]*Limiter{}
c := task.Periodic{
Interval: time.Minute * 3,
Execute: ClearOnlineIP,
}
go func() {
log.WithField("Type", "Limiter").
Debug("ClearOnlineIP started")
time.Sleep(time.Minute * 3)
_ = c.Start()
}()
}
type Limiter struct {
@@ -40,7 +28,6 @@ type Limiter struct {
OldUserOnline *sync.Map // Key: Ip, value: Uid
UUIDtoUID map[string]int // Key: UUID, value: Uid
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
AliveList map[int]int // Key: Uid, value: alive_ip
}
@@ -59,7 +46,6 @@ func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveLi
SpeedLimit: l.SpeedLimit,
UserOnlineIP: new(sync.Map),
UserLimitInfo: new(sync.Map),
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
SpeedLimiter: new(sync.Map),
AliveList: aliveList,
OldUserOnline: new(sync.Map),
@@ -140,10 +126,6 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
// check if ipv4 mapped ipv6
ip = strings.TrimPrefix(ip, "::ffff:")
// ip and conn limiter
if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
return nil, true
}
// check and gen speed limit Bucket
nodeLimit := l.SpeedLimit
userLimit := 0
@@ -169,17 +151,21 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
}
if noSSUDP {
// Store online user for device limit
ipMap := new(sync.Map)
ipMap.Store(ip, uid)
newipMap := new(sync.Map)
newipMap.Store(ip, uid)
aliveIp := l.AliveList[uid]
// If any device is online
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
ipMap := v.(*sync.Map)
if v, loaded := l.UserOnlineIP.LoadOrStore(taguuid, newipMap); loaded {
oldipMap := v.(*sync.Map)
// If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
if deviceLimit > 0 {
if _, loaded := oldipMap.LoadOrStore(ip, uid); !loaded {
if v, loaded := l.OldUserOnline.Load(ip); loaded {
if v.(int) == uid {
l.OldUserOnline.Delete(ip)
}
} else if deviceLimit > 0 {
if deviceLimit <= aliveIp {
ipMap.Delete(ip)
oldipMap.Delete(ip)
return nil, true
}
}
@@ -214,6 +200,7 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
var onlineUser []panel.OnlineUser
l.OldUserOnline = new(sync.Map)
l.UserOnlineIP.Range(func(key, value interface{}) bool {
taguuid := key.(string)
ipMap := value.(*sync.Map)