update example, add realtime option, fix ip limit bug for packet protocol

This commit is contained in:
yuzuki999 2023-05-17 09:46:52 +08:00
parent c6658c2ce1
commit 48da83fc3d
9 changed files with 114 additions and 100 deletions

View File

@ -58,6 +58,7 @@ func (p *Conf) Watch(filePath string, reload func()) error {
case event := <-watcher.Events: case event := <-watcher.Events:
if event.Has(fsnotify.Write) { if event.Has(fsnotify.Write) {
log.Println("config dir changed, reloading...") log.Println("config dir changed, reloading...")
*p = *New()
err := p.LoadFromPath(filePath) err := p.LoadFromPath(filePath)
if err != nil { if err != nil {
log.Printf("reload config error: %s", err) log.Printf("reload config error: %s", err)

View File

@ -1,14 +1,46 @@
package conf package conf
type CertConfig struct { type NodeConfig struct {
CertMode string `yaml:"CertMode"` // none, file, http, dns ApiConfig *ApiConfig `yaml:"ApiConfig"`
RejectUnknownSni bool `yaml:"RejectUnknownSni"` ControllerConfig *ControllerConfig `yaml:"ControllerConfig"`
CertDomain string `yaml:"CertDomain"` }
CertFile string `yaml:"CertFile"`
KeyFile string `yaml:"KeyFile"` type ApiConfig struct {
Provider string `yaml:"Provider"` // alidns, cloudflare, gandi, godaddy.... APIHost string `yaml:"ApiHost"`
Email string `yaml:"Email"` NodeID int `yaml:"NodeID"`
DNSEnv map[string]string `yaml:"DNSEnv"` Key string `yaml:"ApiKey"`
NodeType string `yaml:"NodeType"`
Timeout int `yaml:"Timeout"`
RuleListPath string `yaml:"RuleListPath"`
}
type ControllerConfig struct {
ListenIP string `yaml:"ListenIP"`
SendIP string `yaml:"SendIP"`
EnableDNS bool `yaml:"EnableDNS"`
DNSType string `yaml:"DNSType"`
EnableVless bool `yaml:"EnableVless"`
EnableTls bool `yaml:"EnableTls"`
LimitConfig LimitConfig `yaml:"LimitConfig"`
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"`
DisableGetRule bool `yaml:"DisableGetRule"`
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
EnableFallback bool `yaml:"EnableFallback"`
DisableIVCheck bool `yaml:"DisableIVCheck"`
DisableSniffing bool `yaml:"DisableSniffing"`
FallBackConfigs []*FallBackConfig `yaml:"FallBackConfigs"`
CertConfig *CertConfig `yaml:"CertConfig"`
}
type LimitConfig struct {
EnableRealtime bool `yaml:"EnableRealtime"`
SpeedLimit int `yaml:"SpeedLimit"`
IPLimit int `yaml:"DeviceLimit"`
ConnLimit int `yaml:"ConnLimit"`
EnableIpRecorder bool `yaml:"EnableIpRecorder"`
IpRecorderConfig *IpReportConfig `yaml:"IpRecorderConfig"`
EnableDynamicSpeedLimit bool `yaml:"EnableDynamicSpeedLimit"`
DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"`
} }
type FallBackConfig struct { type FallBackConfig struct {
@ -47,40 +79,13 @@ type DynamicSpeedLimitConfig struct {
ExpireTime int `yaml:"ExpireTime"` ExpireTime int `yaml:"ExpireTime"`
} }
type ControllerConfig struct { type CertConfig struct {
ListenIP string `yaml:"ListenIP"` CertMode string `yaml:"CertMode"` // none, file, http, dns
SendIP string `yaml:"SendIP"` RejectUnknownSni bool `yaml:"RejectUnknownSni"`
EnableDNS bool `yaml:"EnableDNS"` CertDomain string `yaml:"CertDomain"`
DNSType string `yaml:"DNSType"` CertFile string `yaml:"CertFile"`
EnableVless bool `yaml:"EnableVless"` KeyFile string `yaml:"KeyFile"`
EnableTls bool `yaml:"EnableTls"` Provider string `yaml:"Provider"` // alidns, cloudflare, gandi, godaddy....
SpeedLimit int `yaml:"SpeedLimit"` Email string `yaml:"Email"`
IPLimit int `yaml:"DeviceLimit"` DNSEnv map[string]string `yaml:"DNSEnv"`
ConnLimit int `yaml:"ConnLimit"`
DisableUploadTraffic bool `yaml:"DisableUploadTraffic"`
DisableGetRule bool `yaml:"DisableGetRule"`
EnableProxyProtocol bool `yaml:"EnableProxyProtocol"`
EnableFallback bool `yaml:"EnableFallback"`
DisableIVCheck bool `yaml:"DisableIVCheck"`
DisableSniffing bool `yaml:"DisableSniffing"`
FallBackConfigs []*FallBackConfig `yaml:"FallBackConfigs"`
EnableIpRecorder bool `yaml:"EnableIpRecorder"`
IpRecorderConfig *IpReportConfig `yaml:"IpRecorderConfig"`
EnableDynamicSpeedLimit bool `yaml:"EnableDynamicSpeedLimit"`
DynamicSpeedLimitConfig *DynamicSpeedLimitConfig `yaml:"DynamicSpeedLimitConfig"`
CertConfig *CertConfig `yaml:"CertConfig"`
}
type ApiConfig struct {
APIHost string `yaml:"ApiHost"`
NodeID int `yaml:"NodeID"`
Key string `yaml:"ApiKey"`
NodeType string `yaml:"NodeType"`
Timeout int `yaml:"Timeout"`
RuleListPath string `yaml:"RuleListPath"`
}
type NodeConfig struct {
ApiConfig *ApiConfig `yaml:"ApiConfig"`
ControllerConfig *ControllerConfig `yaml:"ControllerConfig"`
} }

View File

@ -13,31 +13,32 @@ ConnetionConfig:
DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second
BufferSize: 64 # The internal cache size of each connection, kB BufferSize: 64 # The internal cache size of each connection, kB
Nodes: Nodes:
- - ApiConfig:
ApiConfig:
ApiHost: "http://127.0.0.1:667" ApiHost: "http://127.0.0.1:667"
ApiKey: "123" ApiKey: "123"
NodeID: 41 NodeID: 41
NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan NodeType: V2ray # Node type: V2ray, Shadowsocks, Trojan
Timeout: 30 # Timeout for the api request Timeout: 30 # Timeout for the api request
EnableVless: false # Enable Vless for V2ray Type
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file RuleListPath: # /etc/XrayR/rulelist Path to local rulelist file
ControllerConfig: ControllerConfig:
ListenIP: 0.0.0.0 # IP address you want to listen ListenIP: 0.0.0.0 # IP address you want to listen
SendIP: 0.0.0.0 # IP address you want to send pacakage SendIP: 0.0.0.0 # IP address you want to send pacakage
EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well EnableDNS: false # Use custom DNS config, Please ensure that you set the dns.json well
DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy DNSType: AsIs # AsIs, UseIP, UseIPv4, UseIPv6, DNS strategy
EnableVless: false # Enable Vless for V2ray Type
EnableProxyProtocol: false # Only works for WebSocket and TCP EnableProxyProtocol: false # Only works for WebSocket and TCP
EnableFallback: false # Only support for Trojan and Vless EnableFallback: false # Only support for Trojan and Vless
FallBackConfigs: # Support multiple fallbacks FallBackConfigs: # Support multiple fallbacks
- - SNI: # TLS SNI(Server Name Indication), Empty for any
SNI: # TLS SNI(Server Name Indication), Empty for any
Alpn: # Alpn, Empty for any Alpn: # Alpn, Empty for any
Path: # HTTP PATH, Empty for any Path: # HTTP PATH, Empty for any
Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details. Dest: 80 # Required, Destination of fallback, check https://xtls.github.io/config/features/fallback.html for details.
ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable ProxyProtocolVer: 0 # Send PROXY protocol version, 0 for dsable
LimitConfig:
EnableRealtime: false # Check device limit on real time
SpeedLimit: 0 # Mbps, Local settings will replace remote settings, 0 means disable
DeviceLimit: 0 # Local settings will replace remote settings, 0 means disable
ConnLimit: 0 # Connecting limit, only working for TCP, 0mean
EnableIpRecorder: false # Enable online ip report EnableIpRecorder: false # Enable online ip report
IpRecorderConfig: IpRecorderConfig:
Type: "Recorder" # Recorder type: Recorder, Redis Type: "Recorder" # Recorder type: Recorder, Redis

View File

@ -2,13 +2,13 @@ package limiter
import "log" import "log"
func ClearPacketOnlineIP() error { func ClearOnlineIP() error {
log.Println("Limiter: Clear packet online ip...") log.Println("Limiter: Clear online ip...")
limitLock.RLock() limitLock.RLock()
for _, l := range limiter { for _, l := range limiter {
l.ConnLimiter.ClearPacketOnlineIP() l.ConnLimiter.ClearOnlineIP()
} }
limitLock.RUnlock() limitLock.RUnlock()
log.Println("Limiter: Clear packet online ip done") log.Println("Limiter: Clear online ip done")
return nil return nil
} }

View File

@ -5,15 +5,16 @@ import (
) )
type ConnLimiter struct { type ConnLimiter struct {
realTime bool realtime bool
ipLimit int ipLimit int
connLimit int connLimit int
count sync.Map // map[string]int count sync.Map // map[string]int
ip sync.Map // map[string]map[string]int ip sync.Map // map[string]map[string]int
} }
func NewConnLimiter(conn int, ip int) *ConnLimiter { func NewConnLimiter(conn int, ip int, realtime bool) *ConnLimiter {
return &ConnLimiter{ return &ConnLimiter{
realtime: realtime,
connLimit: conn, connLimit: conn,
ipLimit: ip, ipLimit: ip,
count: sync.Map{}, count: sync.Map{},
@ -38,11 +39,15 @@ func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bo
} }
// default user map // default user map
ipMap := new(sync.Map) ipMap := new(sync.Map)
if c.realtime {
if isTcp { if isTcp {
ipMap.Store(ip, 2) ipMap.Store(ip, 2)
} else { } else {
ipMap.Store(ip, 1) ipMap.Store(ip, 1)
} }
} else {
ipMap.Store(ip, struct{}{})
}
// check user online ip // check user online ip
if v, ok := c.ip.LoadOrStore(user, ipMap); ok { if v, ok := c.ip.LoadOrStore(user, ipMap); ok {
// have user // have user
@ -50,10 +55,12 @@ func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bo
cn := 0 cn := 0
if online, ok := ips.Load(ip); ok { if online, ok := ips.Load(ip); ok {
// online ip // online ip
if isTcp { if c.realtime {
if online.(int)%2 == 0 && isTcp {
// count add // count add
ips.Store(ip, online.(int)+2) ips.Store(ip, online.(int)+2)
} }
}
} else { } else {
// not online ip // not online ip
ips.Range(func(_, _ interface{}) bool { ips.Range(func(_, _ interface{}) bool {
@ -67,11 +74,15 @@ func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bo
if limit { if limit {
return return
} }
if c.realtime {
if isTcp { if isTcp {
ips.Store(ip, 2) ips.Store(ip, 2)
} else { } else {
ips.Store(ip, 1) ips.Store(ip, 1)
} }
} else {
ips.Store(ip, struct{}{})
}
} }
} }
return return
@ -79,6 +90,9 @@ func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bo
// DelConnCount Delete tcp connection count, no tcp do not use // DelConnCount Delete tcp connection count, no tcp do not use
func (c *ConnLimiter) DelConnCount(user string, ip string) { func (c *ConnLimiter) DelConnCount(user string, ip string) {
if !c.realtime {
return
}
if c.connLimit != 0 { if c.connLimit != 0 {
if v, ok := c.count.Load(user); ok { if v, ok := c.count.Load(user); ok {
if v.(int) == 1 { if v.(int) == 1 {
@ -111,12 +125,18 @@ func (c *ConnLimiter) DelConnCount(user string, ip string) {
} }
} }
// ClearPacketOnlineIP Clear udp,icmp and other packet protocol online ip // ClearOnlineIP Clear udp,icmp and other packet protocol online ip
func (c *ConnLimiter) ClearPacketOnlineIP() { func (c *ConnLimiter) ClearOnlineIP() {
c.ip.Range(func(_, v any) bool { c.ip.Range(func(_, v any) bool {
userIp := v.(*sync.Map) userIp := v.(*sync.Map)
userIp.Range(func(ip, v any) bool { userIp.Range(func(ip, v any) bool {
if c.realtime {
// clear not realtime ip
userIp.Delete(ip)
return true
}
if v.(int) == 1 { if v.(int) == 1 {
// clear packet ip for realtime
userIp.Delete(ip) userIp.Delete(ip)
} }
return true return true

View File

@ -26,7 +26,7 @@ func TestConnLimiter_DelConnCount(t *testing.T) {
func TestConnLimiter_ClearPacketOnlineIP(t *testing.T) { func TestConnLimiter_ClearPacketOnlineIP(t *testing.T) {
t.Log(c.AddConnCount("1", "1", false)) t.Log(c.AddConnCount("1", "1", false))
t.Log(c.AddConnCount("1", "2", false)) t.Log(c.AddConnCount("1", "2", false))
c.ClearPacketOnlineIP() c.ClearOnlineIP()
t.Log(c.AddConnCount("1", "2", true)) t.Log(c.AddConnCount("1", "2", true))
c.DelConnCount("1", "2") c.DelConnCount("1", "2")
t.Log(c.AddConnCount("1", "1", false)) t.Log(c.AddConnCount("1", "1", false))

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/Yuzuki616/V2bX/api/panel" "github.com/Yuzuki616/V2bX/api/panel"
"github.com/Yuzuki616/V2bX/conf"
"github.com/juju/ratelimit" "github.com/juju/ratelimit"
"github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/common/task"
"log" "log"
@ -18,10 +19,10 @@ func Init() {
limiter = map[string]*Limiter{} limiter = map[string]*Limiter{}
c := task.Periodic{ c := task.Periodic{
Interval: time.Minute * 2, Interval: time.Minute * 2,
Execute: ClearPacketOnlineIP, Execute: ClearOnlineIP,
} }
go func() { go func() {
log.Println("Limiter: ClearPacketOnlineIP started") log.Println("Limiter: ClearOnlineIP started")
time.Sleep(time.Minute * 2) time.Sleep(time.Minute * 2)
c.Start() c.Start()
}() }()
@ -43,17 +44,11 @@ type UserLimitInfo struct {
ExpireTime int64 ExpireTime int64
} }
type LimitConfig struct { func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limiter {
SpeedLimit int
IpLimit int
ConnLimit int
}
func AddLimiter(tag string, l *LimitConfig, users []panel.UserInfo) *Limiter {
info := &Limiter{ info := &Limiter{
SpeedLimit: l.SpeedLimit, SpeedLimit: l.SpeedLimit,
UserLimitInfo: new(sync.Map), UserLimitInfo: new(sync.Map),
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IpLimit), ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
SpeedLimiter: new(sync.Map), SpeedLimiter: new(sync.Map),
} }
for i := range users { for i := range users {

View File

@ -58,11 +58,7 @@ func (c *Controller) Start() error {
c.Tag = c.buildNodeTag() c.Tag = c.buildNodeTag()
// add limiter // add limiter
l := limiter.AddLimiter(c.Tag, &limiter.LimitConfig{ l := limiter.AddLimiter(c.Tag, &c.LimitConfig, c.userList)
SpeedLimit: c.SpeedLimit,
IpLimit: c.IPLimit,
ConnLimit: c.ConnLimit,
}, c.userList)
// add rule limiter // add rule limiter
if !c.DisableGetRule { if !c.DisableGetRule {
if err = l.UpdateRule(c.nodeInfo.Rules); err != nil { if err = l.UpdateRule(c.nodeInfo.Rules); err != nil {

View File

@ -88,11 +88,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
if nodeInfoChanged { if nodeInfoChanged {
c.userList = newUserInfo c.userList = newUserInfo
// Add new Limiter // Add new Limiter
l := limiter.AddLimiter(c.Tag, &limiter.LimitConfig{ l := limiter.AddLimiter(c.Tag, &c.LimitConfig, newUserInfo)
SpeedLimit: c.SpeedLimit,
IpLimit: c.IPLimit,
ConnLimit: c.ConnLimit,
}, newUserInfo)
err = c.addNewUser(newUserInfo, newNodeInfo) err = c.addNewUser(newUserInfo, newNodeInfo)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
@ -243,7 +239,7 @@ func (c *Controller) reportUserTraffic() (err error) {
for i := range c.userList { for i := range c.userList {
up, down := c.server.GetUserTraffic(c.buildUserTag(&(c.userList)[i]), true) up, down := c.server.GetUserTraffic(c.buildUserTag(&(c.userList)[i]), true)
if up > 0 || down > 0 { if up > 0 || down > 0 {
if c.EnableDynamicSpeedLimit { if c.LimitConfig.EnableDynamicSpeedLimit {
c.userList[i].Traffic += up + down c.userList[i].Traffic += up + down
} }
userTraffic = append(userTraffic, panel.UserTraffic{ userTraffic = append(userTraffic, panel.UserTraffic{