mirror of
https://github.com/luscis/openlan.git
synced 2025-10-08 10:00:12 +08:00
fea: add qos support for client
This commit is contained in:
@@ -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)
|
||||
|
@@ -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
142
cmd/api/v5/qos.go
Normal 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
9
dist/rootfs/etc/openlan/switch/qos/example.json.example
vendored
Normal file
9
dist/rootfs/etc/openlan/switch/qos/example.json.example
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "example",
|
||||
"qos":{
|
||||
"hi@example": {
|
||||
"inSpeed": 125000,
|
||||
"outSpeed":125000
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
108
pkg/api/qos.go
Normal 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")
|
||||
}
|
@@ -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
13
pkg/cache/qos.go
vendored
Normal 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),
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
23
pkg/config/qos.go
Normal 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() {
|
||||
}
|
@@ -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]
|
||||
}
|
||||
|
@@ -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
9
pkg/schema/qos.go
Normal 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"`
|
||||
}
|
@@ -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
418
pkg/switch/qos.go
Normal 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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user