fea: add qos support for client

This commit is contained in:
teddyzhu15
2024-03-28 11:45:49 +08:00
parent 933f708e02
commit 10867d5921
16 changed files with 858 additions and 27 deletions

View File

@@ -29,6 +29,7 @@ func Commands(app *api.App) {
app.Before = Before
User{}.Commands(app)
ACL{}.Commands(app)
Qos{}.Commands(app)
Device{}.Commands(app)
Lease{}.Commands(app)
Config{}.Commands(app)

View File

@@ -91,6 +91,20 @@ func (u Config) Check(c *cli.Context) error {
}
}
}
out.Info("%15s: %s", "check", "qos")
pattern = filepath.Join(dir, "switch", "qos", "*.json")
if files, err := filepath.Glob(pattern); err == nil {
for _, file := range files {
obj := &config.Qos{}
if err := libol.UnmarshalLoad(obj, file); err != nil {
out.Warn("%15s: %s", filepath.Base(file), err)
} else {
out.Info("%15s: %s", filepath.Base(file), "success")
}
}
}
// Check ACL configurations.
out.Info("%15s: %s", "check", "acl")
pattern = filepath.Join(dir, "switch", "acl", "*.json")

142
cmd/api/v5/qos.go Normal file
View File

@@ -0,0 +1,142 @@
package v5
import (
"github.com/luscis/openlan/cmd/api"
"github.com/luscis/openlan/pkg/schema"
"github.com/urfave/cli/v2"
)
type Qos struct {
Cmd
}
func (q Qos) Commands(app *api.App) {
rule := QosRule{}
app.Command(&cli.Command{
Name: "qos",
Usage: "qos for client in network",
Flags: []cli.Flag{
&cli.StringFlag{Name: "name", Aliases: []string{"n"}},
},
Subcommands: []*cli.Command{
rule.Commands(),
},
})
}
type QosRule struct {
Cmd
}
func (qr QosRule) Url(prefix, name string) string {
return prefix + "/api/network/" + name + "/qos"
}
func (qr QosRule) Add(c *cli.Context) error {
name := c.String("name")
url := qr.Url(c.String("url"), name)
rule := &schema.Qos{
Name: c.String("clientname"),
InSpeed: c.Int64("inspeed"),
OutSpeed: c.Int64("outspeed"),
}
clt := qr.NewHttp(c.String("token"))
if err := clt.PostJSON(url, rule, nil); err != nil {
return err
}
return nil
}
func (qr QosRule) Remove(c *cli.Context) error {
name := c.String("name")
url := qr.Url(c.String("url"), name)
rule := &schema.Qos{
Name: c.String("clientname"),
}
clt := qr.NewHttp(c.String("token"))
if err := clt.DeleteJSON(url, rule, nil); err != nil {
return err
}
return nil
}
func (qr QosRule) Tmpl() string {
return `# total {{ len . }}
{{ps -15 "Name"}} {{ps -15 "Device"}} {{ps -15 "ip"}} {{ps -8 "InSpeed"}} {{ps -8 "OutSpeed"}}
{{- range . }}
{{ps -15 .Name}} {{ps -15 .Device}} {{ps -15 .Ip}} {{pi -8 .InSpeed}} {{pi -8 .OutSpeed}}
{{- end }}
`
}
func (qr QosRule) List(c *cli.Context) error {
name := c.String("name")
url := qr.Url(c.String("url"), name)
clt := qr.NewHttp(c.String("token"))
var items []schema.Qos
if err := clt.GetJSON(url, &items); err != nil {
return err
}
return qr.Out(items, c.String("format"), qr.Tmpl())
}
func (qr QosRule) Save(c *cli.Context) error {
name := c.String("name")
url := qr.Url(c.String("url"), name)
clt := qr.NewHttp(c.String("token"))
if err := clt.PutJSON(url, nil, nil); err != nil {
return err
}
return nil
}
func (qr QosRule) Commands() *cli.Command {
return &cli.Command{
Name: "rule",
Usage: "Access Control Qos Rule",
Subcommands: []*cli.Command{
{
Name: "add",
Usage: "Add a new qos rule for client",
Flags: []cli.Flag{
&cli.StringFlag{Name: "clientname", Aliases: []string{"cn"}},
&cli.StringFlag{Name: "inspeed", Aliases: []string{"is"}},
&cli.StringFlag{Name: "outspeed", Aliases: []string{"os"}},
},
Action: qr.Add,
},
{
Name: "remove",
Usage: "remove a qos rule",
Aliases: []string{"rm"},
Flags: []cli.Flag{
&cli.StringFlag{Name: "clientname", Aliases: []string{"cn"}},
},
Action: qr.Remove,
},
{
Name: "list",
Usage: "Display all qos rules",
Aliases: []string{"ls"},
Action: qr.List,
},
{
Name: "save",
Usage: "Save all qos rules",
Aliases: []string{"sa"},
Action: qr.Save,
},
},
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "example",
"qos":{
"hi@example": {
"inSpeed": 125000,
"outSpeed":125000
}
}
}

View File

@@ -47,6 +47,14 @@ type ZTruster interface {
ListKnock(name string, call func(obj schema.KnockRule))
}
type Qoser interface {
AddQosUser(name string, inSpeed int64, outSpeed int64) error
UpdateQosUser(name string, inSpeed int64, outSpeed int64) error
DelQosUser(name string) error
ListQosUsers(call func(obj schema.Qos))
Save()
}
type Networker interface {
String() string
ID() string
@@ -59,6 +67,7 @@ type Networker interface {
Reload(v Switcher)
Provider() string
ZTruster() ZTruster
Qoser() Qoser
IfAddr() string
ACLer() ACLer
}

108
pkg/api/qos.go Normal file
View File

@@ -0,0 +1,108 @@
package api
import (
"github.com/gorilla/mux"
"github.com/luscis/openlan/pkg/schema"
"net/http"
)
type QosApi struct {
}
func (h QosApi) Router(router *mux.Router) {
router.HandleFunc("/api/network/{id}/qos", h.List).Methods("GET")
router.HandleFunc("/api/network/{id}/qos", h.Add).Methods("POST")
router.HandleFunc("/api/network/{id}/qos", h.Del).Methods("DELETE")
router.HandleFunc("/api/network/{id}/qos", h.Save).Methods("PUT")
}
func (h QosApi) List(w http.ResponseWriter, r *http.Request) {
qosList := make([]schema.Qos, 0, 1024)
vars := mux.Vars(r)
id := vars["id"]
worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
var qos = worker.Qoser()
qos.ListQosUsers(func(obj schema.Qos) {
qosList = append(qosList, obj)
})
ResponseJson(w, qosList)
}
func (h QosApi) Add(w http.ResponseWriter, r *http.Request) {
qos := &schema.Qos{}
if err := GetData(r, qos); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
vars := mux.Vars(r)
id := vars["id"]
worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
if qos != nil {
if err := worker.Qoser().AddQosUser(qos.Name, qos.InSpeed, qos.OutSpeed); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ResponseJson(w, true)
} else {
http.Error(w, vars["id"], http.StatusNotFound)
}
}
func (h QosApi) Del(w http.ResponseWriter, r *http.Request) {
qos := &schema.Qos{}
if err := GetData(r, qos); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
vars := mux.Vars(r)
id := vars["id"]
worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
if qos != nil {
if err := worker.Qoser().DelQosUser(qos.Name); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ResponseJson(w, true)
} else {
http.Error(w, vars["id"], http.StatusNotFound)
}
}
func (h QosApi) Save(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusInternalServerError)
return
}
qos := worker.Qoser()
qos.Save()
ResponseJson(w, "success")
}

View File

@@ -23,6 +23,7 @@ func Add(router *mux.Router, switcher Switcher) {
Log{}.Router(router)
OpenAPI{}.Router(router)
ZTrust{}.Router(router)
QosApi{}.Router(router)
Output{}.Router(router)
ACL{}.Router(router)
}

13
pkg/cache/qos.go vendored Normal file
View File

@@ -0,0 +1,13 @@
package cache
import (
"github.com/luscis/openlan/pkg/libol"
)
type qos struct {
QosConfig *libol.SafeStrMap
}
var pos = &qos{
QosConfig: libol.NewSafeStrMap(1024),
}

View File

@@ -10,6 +10,10 @@ func GetAcl(name string) *ACL {
return switcher.GetACL(name)
}
func GetQos(name string) *Qos {
return switcher.GetQos(name)
}
func Update(obj *Switch) {
switcher = obj
}

View File

@@ -24,6 +24,7 @@ type Network struct {
Dhcp string `json:"dhcp,omitempty"`
Outputs []Output `json:"outputs"`
ZTrust string `json:"ztrust"`
Qos string `json:"qos,omitempty"`
Namespace string `json:"namespace"`
}

23
pkg/config/qos.go Normal file
View File

@@ -0,0 +1,23 @@
package config
import "github.com/luscis/openlan/pkg/libol"
type Qos struct {
File string `json:"file"`
Name string `json:"name"`
Config map[string]*QosLimit `json:"qos,omitempty"`
}
func (q *Qos) Save() {
if err := libol.MarshalSave(q, q.File, true); err != nil {
libol.Error("Switch.Save.Qos %s %s", q.Name, err)
}
}
type QosLimit struct {
InSpeed int64 `json:"inSpeed,omitempty"`
OutSpeed int64 `json:"outSpeed,omitempty"`
}
func (ql *QosLimit) Correct() {
}

View File

@@ -62,6 +62,7 @@ type Switch struct {
Crypt *Crypt `json:"crypt,omitempty"`
Network []*Network `json:"network,omitempty"`
Acl map[string]*ACL `json:"acl,omitempty"`
Qos map[string]*Qos `json:"qos,omitempty"`
FireWall []FlowRule `json:"firewall,omitempty"`
Queue Queue `json:"queue"`
PassFile string `json:"password"`
@@ -75,6 +76,7 @@ type Switch struct {
func NewSwitch() *Switch {
s := &Switch{
Acl: make(map[string]*ACL, 32),
Qos: make(map[string]*Qos, 1024),
}
s.Parse()
s.Initialize()
@@ -100,6 +102,7 @@ func (s *Switch) Initialize() {
func (s *Switch) LoadExt() {
s.LoadAcl()
s.LoadQos()
s.LoadNetwork()
}
@@ -206,6 +209,33 @@ func (s *Switch) LoadNetwork() {
}
}
func (s *Switch) LoadQos() {
files, err := filepath.Glob(s.Dir("qos", "*.json"))
if err != nil {
libol.Error("Switch.LoadQos %s", err)
}
for _, k := range files {
obj := &Qos{
File: k,
}
if err := libol.UnmarshalLoad(obj, k); err != nil {
libol.Error("Switch.LoadQos %s", err)
continue
}
s.Qos[obj.Name] = obj
}
for _, obj := range s.Qos {
for _, rule := range obj.Config {
rule.Correct()
}
if obj.File == "" {
obj.File = s.Dir("acl", obj.Name+".json")
}
}
}
func (s *Switch) LoadAcl() {
files, err := filepath.Glob(s.Dir("acl", "*.json"))
if err != nil {
@@ -231,14 +261,22 @@ func (s *Switch) Load() error {
func (s *Switch) Save() {
tmp := *s
tmp.Acl = nil
tmp.Qos = nil
tmp.Network = nil
if err := libol.MarshalSave(&tmp, tmp.File, true); err != nil {
libol.Error("Switch.Save %s", err)
}
s.SaveAcl()
s.SaveQos()
s.SaveNetwork()
}
func (s *Switch) SaveQos() {
for _, obj := range s.Qos {
obj.Save()
}
}
func (s *Switch) SaveAcl() {
for _, obj := range s.Acl {
obj.Save()
@@ -269,3 +307,7 @@ func (s *Switch) GetNetwork(name string) *Network {
func (s *Switch) GetACL(name string) *ACL {
return s.Acl[name]
}
func (s *Switch) GetQos(name string) *Qos {
return s.Qos[name]
}

View File

@@ -28,33 +28,35 @@ const (
)
type IPRule struct {
Table string
Chain string
Source string
SrcSet string
ToSource string
NoSource string
NoSrcSet string
Dest string
DestSet string
ToDest string
NoDest string
NoDestSet string
Proto string
DstPort string
SrcPort string
Input string
Output string
Comment string
Jump string
SetMss int
Mark uint32
SetMark uint32
Zone uint32
Order string
Match string
CtState string
TcpFlag []string
Table string
Chain string
Source string
SrcSet string
ToSource string
NoSource string
NoSrcSet string
Dest string
DestSet string
ToDest string
NoDest string
NoDestSet string
Proto string
DstPort string
SrcPort string
Input string
Output string
Comment string
Jump string
Limit string
LimitBurst string
SetMss int
Mark uint32
SetMark uint32
Zone uint32
Order string
Match string
CtState string
TcpFlag []string
}
type IPRules []IPRule
@@ -131,6 +133,13 @@ func (ru IPRule) Args() []string {
args = append(args, "-m", "comment", "--comment", ru.Comment)
}
if ru.Limit != "" {
args = append(args, "-m", "limit", "--limit", ru.Limit)
}
if ru.LimitBurst != "" {
args = append(args, "--limit-burst", ru.LimitBurst)
}
if ru.Jump != "" {
jump := strings.ToUpper(ru.Jump)
if jump == "ACCEPT" || jump == "DROP" {

9
pkg/schema/qos.go Normal file
View File

@@ -0,0 +1,9 @@
package schema
type Qos struct {
Name string `json:"name"`
Device string `json:"device"`
Ip string `json:"ip"`
InSpeed int64 `json:"inSpeed"`
OutSpeed int64 `json:"outSpeed"`
}

View File

@@ -53,6 +53,7 @@ type WorkerImpl struct {
setV *cn.IPSet
vpn *OpenVPN
ztrust *ZTrust
qos *QosCtrl
vrf *cn.VRF
table int
br cn.Bridger
@@ -126,6 +127,9 @@ func (w *WorkerImpl) Initialize() {
w.ztrust.Initialize()
}
w.qos = NewQosCtrl(cfg.Name)
w.qos.Initialize()
if cfg.Dhcp == "enable" {
name := cfg.Bridge.Name
if w.br != nil {
@@ -399,6 +403,21 @@ func (w *WorkerImpl) Start(v api.Switcher) {
Comment: "Goto Zero Trust",
})
}
if !(w.qos == nil) {
w.qos.Start()
fire.Mangle.In.AddRule(cn.IPRule{
Input: vpn.Device,
Jump: w.qos.ChainIn(),
Comment: "Goto Qos ChainIn",
})
fire.Mangle.Out.AddRule(cn.IPRule{
Output: vpn.Device,
Jump: w.qos.ChainOut(),
Comment: "Goto Qos ChainOut",
})
}
}
fire.Start()
@@ -486,7 +505,12 @@ func (w *WorkerImpl) Stop() {
if !(w.ztrust == nil) {
w.ztrust.Stop()
}
if !(w.qos == nil) {
w.qos.Stop()
}
w.vpn.Stop()
}
if !(w.dhcp == nil) {
@@ -781,6 +805,10 @@ func (w *WorkerImpl) ZTruster() api.ZTruster {
return w.ztrust
}
func (w *WorkerImpl) Qoser() api.Qoser {
return w.qos
}
func (w *WorkerImpl) IfAddr() string {
return strings.SplitN(w.cfg.Bridge.Address, "/", 2)[0]
}

418
pkg/switch/qos.go Normal file
View File

@@ -0,0 +1,418 @@
package cswitch
import (
"github.com/luscis/openlan/pkg/cache"
"github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
cn "github.com/luscis/openlan/pkg/network"
"github.com/luscis/openlan/pkg/schema"
"strconv"
"strings"
"sync"
"time"
)
//125000 ~ 1Mb/s
type QosUser struct {
QosChainName string
InSpeed int64 // bits
OutSpeed int64 // bits
Name string
Ip string
Device string
qosChainIn *cn.FireWallChain
qosChainOut *cn.FireWallChain
out *libol.SubLogger
}
func (qr *QosUser) RuleName(dir string) string {
nameParts := strings.Split(qr.Name, "@")
return "Qos_" + qr.QosChainName + "-" + dir + "-" + nameParts[0]
}
func (qr *QosUser) InLimitPacket() string {
//bytes / mtu
return strconv.Itoa(int(qr.InSpeed / 1500))
}
func (qr *QosUser) OutLimitPacket() string {
//bytes / mtu
return strconv.Itoa(int(qr.OutSpeed / 1500))
}
func (qr *QosUser) InLimitStr() string {
//bytes / mtu
return qr.InLimitPacket() + "/s"
}
func (qr *QosUser) OutLimitStr() string {
//bytes / mtu
return qr.OutLimitPacket() + "/s"
}
func (qr *QosUser) InLimitRule() cn.IPRule {
return cn.IPRule{
Limit: qr.InLimitStr(),
//LimitBurst: qr.InLimitPacket(),
Comment: "Qos Limit In " + qr.Name,
Jump: "ACCEPT",
}
}
func (qr *QosUser) OutLimitRule() cn.IPRule {
return cn.IPRule{
Limit: qr.OutLimitStr(),
Comment: "Qos Limit Out " + qr.Name,
Jump: "ACCEPT",
}
}
func (qr *QosUser) BuildChainOut(chain *cn.FireWallChain) {
if qr.OutSpeed > 0 {
qr.qosChainOut = cn.NewFireWallChain(qr.RuleName("out"), cn.TMangle, "")
qr.qosChainOut.AddRule(qr.OutLimitRule())
qr.qosChainOut.AddRule(cn.IPRule{
Comment: "Qos Default Drop",
Jump: "DROP",
})
qr.qosChainOut.Install()
qr.BuildChainOutJump(chain)
}
}
func (qr *QosUser) BuildChainOutJump(chain *cn.FireWallChain) {
if qr.Ip != "" {
if err := chain.AddRuleX(cn.IPRule{
Comment: "Qos Jump",
Jump: qr.RuleName("out"),
Dest: qr.Ip,
}); err != nil {
qr.out.Warn("Qos.Add Out Rule: %s", err)
}
}
}
func (qr *QosUser) ClearChainOutJump(chain *cn.FireWallChain) {
if err := chain.DelRuleX(cn.IPRule{
Comment: "Qos Jump",
Jump: qr.RuleName("out"),
Dest: qr.Ip,
}); err != nil {
qr.out.Warn("Qos.Del Out Rule: %s", err)
}
}
func (qr *QosUser) BuildChainIn(chain *cn.FireWallChain) {
if qr.InSpeed > 0 {
qr.qosChainIn = cn.NewFireWallChain(qr.RuleName("in"), cn.TMangle, "")
qr.qosChainIn.AddRule(qr.InLimitRule())
qr.qosChainIn.AddRule(cn.IPRule{
Comment: "Qos Default Drop",
Jump: "DROP",
})
qr.qosChainIn.Install()
qr.BuildChainInJump(chain)
}
}
func (qr *QosUser) BuildChainInJump(chain *cn.FireWallChain) {
if qr.Ip != "" {
if err := chain.AddRuleX(cn.IPRule{
Comment: "Qos Jump",
Jump: qr.RuleName("in"),
Source: qr.Ip,
}); err != nil {
qr.out.Warn("Qos.Add In Rule: %s", err)
}
}
}
func (qr *QosUser) ClearChainInJump(chain *cn.FireWallChain) {
if err := chain.DelRuleX(cn.IPRule{
Comment: "Qos Jump",
Jump: qr.RuleName("in"),
Source: qr.Ip,
}); err != nil {
qr.out.Warn("Qos.Del In Rule: %s", err)
}
}
func (qr *QosUser) Start(chainIn *cn.FireWallChain, chainOut *cn.FireWallChain) {
qr.BuildChainIn(chainIn)
qr.BuildChainOut(chainOut)
}
func (qr *QosUser) ReBuild(chainIn *cn.FireWallChain, chainOut *cn.FireWallChain) {
qr.Clear(chainIn, chainOut)
qr.Start(chainIn, chainOut)
}
func (qr *QosUser) ClearChainIn(chain *cn.FireWallChain) {
if qr.qosChainIn != nil {
qr.ClearChainInJump(chain)
qr.qosChainIn.Cancel()
qr.qosChainIn = nil
}
}
func (qr *QosUser) ClearChainOut(chain *cn.FireWallChain) {
if qr.qosChainOut != nil {
qr.ClearChainOutJump(chain)
qr.qosChainOut.Cancel()
qr.qosChainOut = nil
}
}
func (qr *QosUser) Clear(chainIn *cn.FireWallChain, chainOut *cn.FireWallChain) {
qr.ClearChainIn(chainIn)
qr.ClearChainOut(chainOut)
}
func (qr *QosUser) Update(chainIn *cn.FireWallChain, chainOut *cn.FireWallChain, inSpeed int64, outSpeed int64, device string, ip string) {
ipChange := false
if qr.Ip != ip {
ipChange = true
qr.Ip = ip
}
if ipChange {
qr.ClearChainInJump(chainIn)
qr.ClearChainOutJump(chainOut)
qr.BuildChainOutJump(chainOut)
qr.BuildChainInJump(chainIn)
}
if qr.InSpeed != inSpeed {
qr.InSpeed = inSpeed
qr.ClearChainIn(chainIn)
qr.BuildChainIn(chainIn)
}
if qr.OutSpeed != outSpeed {
qr.OutSpeed = outSpeed
qr.ClearChainOut(chainOut)
qr.BuildChainOut(chainOut)
}
}
type QosCtrl struct {
Name string
Rules map[string]*QosUser
chainIn *cn.FireWallChain
chainOut *cn.FireWallChain
out *libol.SubLogger
lock sync.Mutex
}
func NewQosCtrl(name string) *QosCtrl {
return &QosCtrl{
Name: name,
Rules: make(map[string]*QosUser, 1024),
out: libol.NewSubLogger("Qos"),
}
}
func (q *QosCtrl) ChainIn() string {
return "Qos_" + q.Name + "-in"
}
func (q *QosCtrl) ChainOut() string {
return "Qos_" + q.Name + "-out"
}
func (q *QosCtrl) Initialize() {
//q.Start()
q.chainIn = cn.NewFireWallChain(q.ChainIn(), cn.TMangle, "")
q.chainOut = cn.NewFireWallChain(q.ChainOut(), cn.TMangle, "")
qosCfg := config.GetQos(q.Name)
if qosCfg != nil && len(qosCfg.Config) > 0 {
for name, limit := range qosCfg.Config {
qr := &QosUser{
QosChainName: q.Name,
Name: name,
InSpeed: limit.InSpeed,
OutSpeed: limit.OutSpeed,
Ip: "",
out: libol.NewSubLogger("Qos_" + name),
}
q.Rules[name] = qr
}
}
}
func (q *QosCtrl) Start() {
q.out.Info("Qos.Start")
q.chainIn.Install()
q.chainOut.Install()
if len(q.Rules) > 0 {
for _, rule := range q.Rules {
rule.Start(q.chainIn, q.chainOut)
}
}
libol.Go(q.Update)
}
func (q *QosCtrl) Stop() {
q.out.Info("Qos.Stop")
if len(q.Rules) != 0 {
for _, rule := range q.Rules {
rule.Clear(q.chainIn, q.chainOut)
}
}
q.chainIn.Cancel()
q.chainOut.Cancel()
}
func (q *QosCtrl) DelUserRule(name string) {
q.lock.Lock()
defer q.lock.Unlock()
if rule, ok := q.Rules[name]; ok {
rule.Clear(q.chainIn, q.chainOut)
delete(q.Rules, name)
}
}
func (q *QosCtrl) FindClient(name string) *schema.VPNClient {
for n := range cache.Network.List() {
if n == nil {
break
}
for client := range cache.VPNClient.List(n.Name) {
if client == nil {
break
}
if client.Name == name {
return client
}
}
}
return nil
}
func (q *QosCtrl) AddOrUpdateQosUser(name string, inSpeed int64, outSpeed int64) {
q.lock.Lock()
defer q.lock.Unlock()
client := q.FindClient(name)
device := ""
var ip = ""
if client != nil {
device = client.Device
ip = client.Address
}
if rule, ok := q.Rules[name]; ok {
rule.Update(q.chainIn, q.chainOut, inSpeed, outSpeed, device, ip)
} else {
rule = &QosUser{
QosChainName: q.Name,
Name: name,
InSpeed: inSpeed,
OutSpeed: outSpeed,
Ip: ip,
out: libol.NewSubLogger("Qos_" + name),
}
rule.Start(q.chainIn, q.chainOut)
q.Rules[name] = rule
}
}
func (q *QosCtrl) ClientUpdate() {
clients := make([]schema.VPNClient, 0, 1024)
for n := range cache.Network.List() {
if n == nil {
break
}
for client := range cache.VPNClient.List(n.Name) {
if client == nil {
break
}
clients = append(clients, *client)
}
}
for _, rule := range q.Rules {
var existClient *schema.VPNClient
for _, client := range clients {
if client.Name == rule.Name {
existClient = &client
break
}
}
if existClient != nil {
rule.Update(q.chainIn, q.chainOut, rule.InSpeed, rule.OutSpeed, existClient.Device, existClient.Address)
} else {
if rule.Ip != "" {
rule.ClearChainInJump(q.chainIn)
rule.ClearChainOutJump(q.chainOut)
rule.Ip = ""
}
}
}
}
func (q *QosCtrl) Update() {
for {
q.ClientUpdate()
time.Sleep(time.Second * 5)
}
}
func (q *QosCtrl) Save() {
cfg := config.GetQos(q.Name)
cfg.Config = make(map[string]*config.QosLimit, 1024)
for _, rule := range q.Rules {
ql := &config.QosLimit{
InSpeed: rule.InSpeed,
OutSpeed: rule.OutSpeed,
}
cfg.Config[rule.Name] = ql
}
cfg.Save()
}
func (q *QosCtrl) AddQosUser(name string, inSpeed int64, outSpeed int64) error {
q.AddOrUpdateQosUser(name, inSpeed, outSpeed)
return nil
}
func (q *QosCtrl) UpdateQosUser(name string, inSpeed int64, outSpeed int64) error {
q.AddOrUpdateQosUser(name, inSpeed, outSpeed)
return nil
}
func (q *QosCtrl) DelQosUser(name string) error {
q.DelUserRule(name)
return nil
}
func (q *QosCtrl) ListQosUsers(call func(obj schema.Qos)) {
for _, rule := range q.Rules {
obj := schema.Qos{
Name: rule.Name,
Device: rule.Device,
InSpeed: rule.InSpeed,
OutSpeed: rule.OutSpeed,
Ip: rule.Ip,
}
call(obj)
}
}