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

@@ -47,6 +47,8 @@ type IPRule struct {
Output string
Comment string
Jump string
Limit string
LimitBurst string
SetMss int
Mark uint32
SetMark uint32
@@ -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)
}
}