mirror of
				https://github.com/luscis/openlan.git
				synced 2025-10-30 20:16:22 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			337 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			337 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| //125000 ~ 1Mb/s
 | |
| 
 | |
| type QosUser struct {
 | |
| 	QosChainName string
 | |
| 	InSpeed      float64 // Mbit
 | |
| 	Name         string
 | |
| 	Ip           string
 | |
| 	Device       string
 | |
| 	uniqueName   string
 | |
| 	qosChainIn   *cn.FireWallChain
 | |
| 	out          *libol.SubLogger
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) RuleName(dir string) string {
 | |
| 
 | |
| 	return "Qos_" + qr.QosChainName + "-" + dir + "-" + qr.uniqueName
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) InLimitPacket() string {
 | |
| 	//Mbit * 125000 / mtu
 | |
| 	return strconv.Itoa(int((qr.InSpeed * 125000) / 1300))
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) InLimitStr() string {
 | |
| 	return qr.InLimitPacket() + "/s"
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) InLimitRule() cn.IPRule {
 | |
| 	return cn.IPRule{
 | |
| 		Limit:      qr.InLimitStr(),
 | |
| 		LimitBurst: "100",
 | |
| 		Comment:    "Qos Limit In " + qr.Name,
 | |
| 		Jump:       "ACCEPT",
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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) {
 | |
| 	qr.BuildChainIn(chainIn)
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) ReBuild(chainIn *cn.FireWallChain) {
 | |
| 	qr.Clear(chainIn)
 | |
| 	qr.Start(chainIn)
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) ClearChainIn(chain *cn.FireWallChain) {
 | |
| 	if qr.qosChainIn != nil {
 | |
| 		qr.ClearChainInJump(chain)
 | |
| 		qr.qosChainIn.Cancel()
 | |
| 		qr.qosChainIn = nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) Clear(chainIn *cn.FireWallChain) {
 | |
| 	qr.ClearChainIn(chainIn)
 | |
| }
 | |
| 
 | |
| func (qr *QosUser) Update(chainIn *cn.FireWallChain, inSpeed float64, device string, ip string) {
 | |
| 
 | |
| 	changed := false
 | |
| 	qr.Device = device
 | |
| 	if qr.Ip != ip {
 | |
| 		changed = true
 | |
| 		qr.Ip = ip
 | |
| 	}
 | |
| 
 | |
| 	if changed {
 | |
| 		qr.ClearChainInJump(chainIn)
 | |
| 		qr.BuildChainInJump(chainIn)
 | |
| 	}
 | |
| 
 | |
| 	if qr.InSpeed != inSpeed {
 | |
| 		qr.InSpeed = inSpeed
 | |
| 		qr.ClearChainIn(chainIn)
 | |
| 		qr.BuildChainIn(chainIn)
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| type QosCtrl struct {
 | |
| 	Name    string
 | |
| 	Rules   map[string]*QosUser
 | |
| 	chainIn *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) Initialize() {
 | |
| 	//q.Start()
 | |
| 	q.chainIn = cn.NewFireWallChain(q.ChainIn(), 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,
 | |
| 				Ip:           "",
 | |
| 				uniqueName:   libol.GenString(7),
 | |
| 				out:          libol.NewSubLogger("Qos_" + name),
 | |
| 			}
 | |
| 			q.Rules[name] = qr
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (q *QosCtrl) Start() {
 | |
| 	q.out.Info("Qos.Start")
 | |
| 	q.chainIn.Install()
 | |
| 
 | |
| 	if len(q.Rules) > 0 {
 | |
| 		for _, rule := range q.Rules {
 | |
| 			rule.Start(q.chainIn)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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.chainIn.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)
 | |
| 		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 float64) {
 | |
| 	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, inSpeed, device, ip)
 | |
| 	} else {
 | |
| 
 | |
| 		rule = &QosUser{
 | |
| 			QosChainName: q.Name,
 | |
| 			Name:         name,
 | |
| 			InSpeed:      inSpeed,
 | |
| 			Ip:           ip,
 | |
| 			uniqueName:   libol.GenString(7),
 | |
| 			out:          libol.NewSubLogger("Qos_" + name),
 | |
| 		}
 | |
| 		rule.Start(q.chainIn)
 | |
| 
 | |
| 		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, rule.InSpeed, existClient.Device, existClient.Address)
 | |
| 		} else {
 | |
| 			if rule.Ip != "" {
 | |
| 				rule.ClearChainInJump(q.chainIn)
 | |
| 				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,
 | |
| 		}
 | |
| 		cfg.Config[rule.Name] = ql
 | |
| 	}
 | |
| 	cfg.Save()
 | |
| }
 | |
| 
 | |
| func (q *QosCtrl) AddQosUser(name string, inSpeed float64) error {
 | |
| 
 | |
| 	q.AddOrUpdateQosUser(name, inSpeed)
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| func (q *QosCtrl) UpdateQosUser(name string, inSpeed float64) error {
 | |
| 
 | |
| 	q.AddOrUpdateQosUser(name, inSpeed)
 | |
| 
 | |
| 	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,
 | |
| 			Ip:      rule.Ip,
 | |
| 		}
 | |
| 		call(obj)
 | |
| 	}
 | |
| }
 | 
