From c542a184cc23837d4a52ca460c4388118988c00d Mon Sep 17 00:00:00 2001 From: Daniel Ding Date: Mon, 8 Jan 2024 20:56:21 +0800 Subject: [PATCH] fea: add statics for output --- pkg/access/worker.go | 6 ++--- pkg/api/link.go | 2 +- pkg/api/output.go | 43 +++++++++++++++++++++++++++++++++ pkg/cache/output.go | 47 +++++++++++++++++++++++++++++++++++++ pkg/libol/utils.go | 4 ++-- pkg/models/ipsec.go | 32 ++++--------------------- pkg/models/ipsec_linux.go | 29 +++++++++++++++++++++++ pkg/models/ipsec_others.go | 6 +++++ pkg/models/output.go | 19 +++++++++++++++ pkg/models/output_linux.go | 14 +++++++++++ pkg/models/output_others.go | 6 +++++ pkg/models/schema.go | 18 +++++++++++--- pkg/public/index.html | 18 +++++++++++++- pkg/schema/index.go | 1 + pkg/schema/ipsec.go | 8 +++---- pkg/schema/link.go | 6 ++--- pkg/schema/output.go | 13 ++++++++++ pkg/schema/point.go | 6 ++--- pkg/switch/http.go | 31 ++++++++++++------------ pkg/switch/network.go | 22 +++++++++++++++++ 20 files changed, 268 insertions(+), 63 deletions(-) create mode 100755 pkg/api/output.go create mode 100755 pkg/cache/output.go create mode 100755 pkg/models/ipsec_linux.go create mode 100644 pkg/models/ipsec_others.go create mode 100755 pkg/models/output.go create mode 100755 pkg/models/output_linux.go create mode 100755 pkg/models/output_others.go create mode 100644 pkg/schema/output.go diff --git a/pkg/access/worker.go b/pkg/access/worker.go index f0cdb89..41009ec 100755 --- a/pkg/access/worker.go +++ b/pkg/access/worker.go @@ -237,9 +237,9 @@ func (w *Worker) FlushStatus() { } sts := client.Statistics() status := &schema.Point{ - RxBytes: sts[libol.CsRecvOkay], - TxBytes: sts[libol.CsSendOkay], - ErrPkt: sts[libol.CsSendError], + RxBytes: uint64(sts[libol.CsRecvOkay]), + TxBytes: uint64(sts[libol.CsSendOkay]), + ErrPkt: uint64(sts[libol.CsSendError]), Uptime: client.UpTime(), State: client.Status().String(), Device: device.Name(), diff --git a/pkg/api/link.go b/pkg/api/link.go index 095f76a..d900d77 100755 --- a/pkg/api/link.go +++ b/pkg/api/link.go @@ -32,8 +32,8 @@ func (h Link) List(w http.ResponseWriter, r *http.Request) { func (h Link) Get(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - libol.Info("Link.Get %s", vars["id"]) + libol.Debug("Link.Get %s", vars["id"]) link := cache.Link.Get(vars["id"]) if link != nil { ResponseJson(w, models.NewLinkSchema(link)) diff --git a/pkg/api/output.go b/pkg/api/output.go new file mode 100755 index 0000000..e9492c2 --- /dev/null +++ b/pkg/api/output.go @@ -0,0 +1,43 @@ +package api + +import ( + "net/http" + + "github.com/gorilla/mux" + "github.com/luscis/openlan/pkg/cache" + "github.com/luscis/openlan/pkg/libol" + "github.com/luscis/openlan/pkg/models" + "github.com/luscis/openlan/pkg/schema" +) + +type Output struct { + Switcher Switcher +} + +func (h Output) Router(router *mux.Router) { + router.HandleFunc("/api/output", h.List).Methods("GET") + router.HandleFunc("/api/output/{id}", h.Get).Methods("GET") +} + +func (h Output) List(w http.ResponseWriter, r *http.Request) { + outputs := make([]schema.Output, 0, 1024) + for l := range cache.Output.List() { + if l == nil { + break + } + outputs = append(outputs, models.NewOutputSchema(l)) + } + ResponseJson(w, outputs) +} + +func (h Output) Get(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + libol.Debug("Output.Get %s", vars["id"]) + output := cache.Output.Get(vars["id"]) + if output != nil { + ResponseJson(w, models.NewOutputSchema(output)) + } else { + http.Error(w, vars["id"], http.StatusNotFound) + } +} diff --git a/pkg/cache/output.go b/pkg/cache/output.go new file mode 100755 index 0000000..5d143f7 --- /dev/null +++ b/pkg/cache/output.go @@ -0,0 +1,47 @@ +package cache + +import ( + "github.com/luscis/openlan/pkg/libol" + "github.com/luscis/openlan/pkg/models" +) + +type output struct { + outputs *libol.SafeStrMap +} + +func (p *output) Init(size int) { + p.outputs = libol.NewSafeStrMap(size) +} + +func (p *output) Add(uuid string, output *models.Output) { + _ = p.outputs.Set(uuid, output) +} + +func (p *output) Get(key string) *models.Output { + ret := p.outputs.Get(key) + if ret != nil { + return ret.(*models.Output) + } + return nil +} + +func (p *output) Del(key string) { + p.outputs.Del(key) +} + +func (p *output) List() <-chan *models.Output { + c := make(chan *models.Output, 128) + go func() { + p.outputs.Iter(func(k string, v interface{}) { + m := v.(*models.Output) + m.Update() + c <- m + }) + c <- nil //Finish channel by nil. + }() + return c +} + +var Output = output{ + outputs: libol.NewSafeStrMap(1024), +} diff --git a/pkg/libol/utils.go b/pkg/libol/utils.go index dc5ef6b..17753d8 100755 --- a/pkg/libol/utils.go +++ b/pkg/libol/utils.go @@ -214,8 +214,8 @@ func PrettyTime(t int64) string { return fmt.Sprintf("%s%dd%dh", s, days, hours%24) } -func PrettyBytes(b int64) string { - split := func(_v int64, _m int64) (i int64, d int) { +func PrettyBytes(b uint64) string { + split := func(_v uint64, _m uint64) (i uint64, d int) { _d := float64(_v%_m) / float64(_m) return _v / _m, int(_d * 100) //move two decimal to integer } diff --git a/pkg/models/ipsec.go b/pkg/models/ipsec.go index 38b98a3..a54d12f 100755 --- a/pkg/models/ipsec.go +++ b/pkg/models/ipsec.go @@ -1,13 +1,11 @@ -// +build linux - package models import ( "fmt" - "github.com/luscis/openlan/pkg/libol" + "time" + "github.com/luscis/openlan/pkg/schema" nl "github.com/vishvananda/netlink" - "time" ) type Esp struct { @@ -39,26 +37,6 @@ type EspState struct { Out *nl.XfrmState } -func (l *EspState) Update() { - used := int64(0) - if xss, err := nl.XfrmStateGet(l.In); xss != nil { - l.TxBytes = int64(xss.Statistics.Bytes) - l.TxPackages = int64(xss.Statistics.Packets) - used = int64(xss.Statistics.UseTime) - } else { - libol.Debug("EspState.Update %s", err) - } - if xss, err := nl.XfrmStateGet(l.Out); xss != nil { - l.RxBytes = int64(xss.Statistics.Bytes) - l.RxPackages = int64(xss.Statistics.Packets) - } else { - libol.Debug("EspState.Update %s", err) - } - if used > 0 { - l.AliveTime = time.Now().Unix() - used - } -} - func (l *EspState) ID() string { return fmt.Sprintf("spi:%d %s-%s", l.Spi, l.Local, l.Remote) } @@ -94,9 +72,6 @@ type EspPolicy struct { Out *nl.XfrmPolicy } -func (l *EspPolicy) Update() { -} - func (l *EspPolicy) ID() string { return fmt.Sprintf("spi:%d %s-%s", l.Spi, l.Source, l.Dest) } @@ -105,6 +80,9 @@ func (l *EspPolicy) String() string { return fmt.Sprintf("{Spi: %d Source: %s Dest: %s}", l.Spi, l.Source, l.Dest) } +func (l *EspPolicy) Update() { +} + func NewEspPolicySchema(e *EspPolicy) schema.EspPolicy { e.Update() se := schema.EspPolicy{ diff --git a/pkg/models/ipsec_linux.go b/pkg/models/ipsec_linux.go new file mode 100755 index 0000000..1b20b85 --- /dev/null +++ b/pkg/models/ipsec_linux.go @@ -0,0 +1,29 @@ +package models + +import ( + "github.com/luscis/openlan/pkg/libol" + nl "github.com/vishvananda/netlink" + "time" +) + +func (l *EspState) Update() { + used := int64(0) + if xss, err := nl.XfrmStateGet(l.In); xss != nil { + l.TxBytes = xss.Statistics.Bytes + l.TxPackages = xss.Statistics.Packets + used = int64(xss.Statistics.UseTime) + } else { + libol.Debug("EspState.Update %s", err) + } + + if xss, err := nl.XfrmStateGet(l.Out); xss != nil { + l.RxBytes = xss.Statistics.Bytes + l.RxPackages = xss.Statistics.Packets + } else { + libol.Debug("EspState.Update %s", err) + } + + if used > 0 { + l.AliveTime = time.Now().Unix() - used + } +} diff --git a/pkg/models/ipsec_others.go b/pkg/models/ipsec_others.go new file mode 100644 index 0000000..7d5f5ee --- /dev/null +++ b/pkg/models/ipsec_others.go @@ -0,0 +1,6 @@ +// +build !linux + +package models + +func (l *EspState) Update() { +} diff --git a/pkg/models/output.go b/pkg/models/output.go new file mode 100755 index 0000000..66e20e9 --- /dev/null +++ b/pkg/models/output.go @@ -0,0 +1,19 @@ +package models + +import "time" + +type Output struct { + Network string + Protocol string + Connection string + Vlan int + Device string + RxBytes uint64 + TxBytes uint64 + ErrPkt uint64 + NewTime int64 +} + +func (o *Output) UpTime() int64 { + return time.Now().Unix() - o.NewTime +} diff --git a/pkg/models/output_linux.go b/pkg/models/output_linux.go new file mode 100755 index 0000000..8b5040c --- /dev/null +++ b/pkg/models/output_linux.go @@ -0,0 +1,14 @@ +package models + +import ( + nl "github.com/vishvananda/netlink" +) + +func (l *Output) Update() { + if link, err := nl.LinkByName(l.Device); err == nil { + sts := link.Attrs().Statistics + l.RxBytes = sts.RxBytes + l.TxBytes = sts.TxBytes + } + return +} diff --git a/pkg/models/output_others.go b/pkg/models/output_others.go new file mode 100755 index 0000000..e7bb6d2 --- /dev/null +++ b/pkg/models/output_others.go @@ -0,0 +1,6 @@ +// +build !linux + +package models + +func (l *Output) Update() { +} diff --git a/pkg/models/schema.go b/pkg/models/schema.go index d3f2ac9..468b4b3 100755 --- a/pkg/models/schema.go +++ b/pkg/models/schema.go @@ -16,9 +16,9 @@ func NewPointSchema(p *Point) schema.Point { Protocol: p.Protocol, Remote: client.String(), Device: dev.Name(), - RxBytes: sts[libol.CsRecvOkay], - TxBytes: sts[libol.CsSendOkay], - ErrPkt: sts[libol.CsSendError], + RxBytes: uint64(sts[libol.CsRecvOkay]), + TxBytes: uint64(sts[libol.CsSendOkay]), + ErrPkt: uint64(sts[libol.CsSendError]), State: client.Status().String(), Network: p.Network, AliveTime: client.AliveTime(), @@ -116,3 +116,15 @@ func NewNetworkSchema(n *Network) schema.Network { } return sn } + +func NewOutputSchema(o *Output) schema.Output { + return schema.Output{ + Network: o.Network, + Protocol: o.Protocol, + Connection: o.Connection, + Device: o.Device, + RxBytes: o.RxBytes, + TxBytes: o.RxBytes, + AliveTime: o.UpTime(), + } +} diff --git a/pkg/public/index.html b/pkg/public/index.html index e6e6593..cc51bd7 100755 --- a/pkg/public/index.html +++ b/pkg/public/index.html @@ -150,12 +150,28 @@ + {{ range .Outputs }} + + output + {{ prettyTime .AliveTime }} + {{ .Device }} + -- + {{ .Protocol }} + {{ .Connection }} + {{ prettyBytes .RxBytes }}/{{ prettyBytes .TxBytes }} + + {{ if .RxBytes }}success + {{ else }}unknown + {{ end}} + + + {{ end }} {{ range .States }} {{ .Name }} {{ prettyTime .AliveTime }} spi:{{ .Spi }} - {{ .Local }} + -- esp {{ .Remote }} {{ prettyBytes .RxBytes }}/{{ prettyBytes .TxBytes }} diff --git a/pkg/schema/index.go b/pkg/schema/index.go index 97fbd74..27624ed 100755 --- a/pkg/schema/index.go +++ b/pkg/schema/index.go @@ -10,6 +10,7 @@ type Index struct { Network []Network `json:"network"` Clients []VPNClient `json:"clients"` States []EspState `json:"states"` + Outputs []Output `json:"output"` } type Ctrl struct { diff --git a/pkg/schema/ipsec.go b/pkg/schema/ipsec.go index bdaefb3..f9474ed 100644 --- a/pkg/schema/ipsec.go +++ b/pkg/schema/ipsec.go @@ -20,10 +20,10 @@ type EspState struct { Crypt string `json:"crypt"` Encap string `json:"encap" ` RemotePort int `json:"remotePort"` - TxBytes int64 `json:"txBytes"` - TxPackages int64 `json:"txPackages"` - RxBytes int64 `json:"rxBytes"` - RxPackages int64 `json:"rxPackages"` + TxBytes uint64 `json:"txBytes"` + TxPackages uint64 `json:"txPackages"` + RxBytes uint64 `json:"rxBytes"` + RxPackages uint64 `json:"rxPackages"` } type EspPolicy struct { diff --git a/pkg/schema/link.go b/pkg/schema/link.go index dca304f..e73f22d 100755 --- a/pkg/schema/link.go +++ b/pkg/schema/link.go @@ -9,9 +9,9 @@ type Link struct { Protocol string `json:"protocol"` Server string `json:"server"` Device string `json:"device"` - RxBytes int64 `json:"rxBytes"` - TxBytes int64 `json:"txBytes"` - ErrPkt int64 `json:"errors"` + RxBytes uint64 `json:"rxBytes"` + TxBytes uint64 `json:"txBytes"` + ErrPkt uint64 `json:"errors"` State string `json:"state"` AliveTime int64 `json:"aliveTime"` } diff --git a/pkg/schema/output.go b/pkg/schema/output.go new file mode 100644 index 0000000..be1ad27 --- /dev/null +++ b/pkg/schema/output.go @@ -0,0 +1,13 @@ +package schema + +type Output struct { + Network string `json:"network"` + Protocol string `json:"protocol"` + Connection string `json:"connection"` + Vlan int `json:"vlan"` + Device string `json:"device"` + RxBytes uint64 `json:"rxBytes"` + TxBytes uint64 `json:"txBytes"` + ErrPkt uint64 `json:"errors"` + AliveTime int64 `json:"aliveTime"` +} diff --git a/pkg/schema/point.go b/pkg/schema/point.go index e4c3aff..4910679 100755 --- a/pkg/schema/point.go +++ b/pkg/schema/point.go @@ -10,9 +10,9 @@ type Point struct { Remote string `json:"remote"` Switch string `json:"switch,omitempty"` Device string `json:"device"` - RxBytes int64 `json:"rxBytes"` - TxBytes int64 `json:"txBytes"` - ErrPkt int64 `json:"errors"` + RxBytes uint64 `json:"rxBytes"` + TxBytes uint64 `json:"txBytes"` + ErrPkt uint64 `json:"errors"` State string `json:"state"` AliveTime int64 `json:"aliveTime"` System string `json:"system"` diff --git a/pkg/switch/http.go b/pkg/switch/http.go index 07a8f37..d0aefb2 100755 --- a/pkg/switch/http.go +++ b/pkg/switch/http.go @@ -305,6 +305,19 @@ func (h *Http) getIndex(body *schema.Index) *schema.Index { jj := body.States[j] return ii.Spi > jj.Spi }) + + // display esp state + for s := range cache.Output.List() { + if s == nil { + break + } + body.Outputs = append(body.Outputs, models.NewOutputSchema(s)) + } + sort.SliceStable(body.Outputs, func(i, j int) bool { + ii := body.Outputs[i] + jj := body.Outputs[j] + return ii.Device > jj.Device + }) return body } @@ -327,14 +340,7 @@ func (h *Http) ParseFiles(w http.ResponseWriter, name string, data interface{}) } func (h *Http) IndexHtml(w http.ResponseWriter, r *http.Request) { - body := schema.Index{ - Points: make([]schema.Point, 0, 128), - Links: make([]schema.Link, 0, 128), - Neighbors: make([]schema.Neighbor, 0, 128), - OnLines: make([]schema.OnLine, 0, 128), - Clients: make([]schema.VPNClient, 0, 128), - States: make([]schema.EspState, 0, 128), - } + body := schema.Index{} h.getIndex(&body) file := h.getFile("/index.html") if err := h.ParseFiles(w, file, &body); err != nil { @@ -343,14 +349,7 @@ func (h *Http) IndexHtml(w http.ResponseWriter, r *http.Request) { } func (h *Http) GetIndex(w http.ResponseWriter, r *http.Request) { - body := schema.Index{ - Points: make([]schema.Point, 0, 128), - Links: make([]schema.Link, 0, 128), - Neighbors: make([]schema.Neighbor, 0, 128), - OnLines: make([]schema.OnLine, 0, 128), - Network: make([]schema.Network, 0, 128), - Clients: make([]schema.VPNClient, 0, 128), - } + body := schema.Index{} h.getIndex(&body) api.ResponseJson(w, body) } diff --git a/pkg/switch/network.go b/pkg/switch/network.go index fe760c8..535119e 100755 --- a/pkg/switch/network.go +++ b/pkg/switch/network.go @@ -155,7 +155,17 @@ func (w *WorkerImpl) AddPhysical(bridge string, vlan int, output string) { func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) { name := port.name values := strings.SplitN(name, ":", 6) + + out := &models.Output{ + Network: w.cfg.Name, + NewTime: time.Now().Unix(), + } if values[0] == "gre" { + if len(values) < 3 { + w.out.Error("WorkerImpl.LinkAdd %s wrong", name) + return + } + if port.link == "" { port.link = co.GenName("gre") } @@ -175,11 +185,14 @@ func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) { w.out.Error("WorkerImpl.LinkAdd %s %s", name, err) return } + out.Protocol = "gre" + out.Connection = fmt.Sprintf("%s:%s", values[1], values[2]) } else if values[0] == "vxlan" { if len(values) < 3 { w.out.Error("WorkerImpl.LinkAdd %s wrong", name) return } + if port.link == "" { port.link = co.GenName("vxn") } @@ -202,9 +215,15 @@ func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) { w.out.Error("WorkerImpl.LinkAdd %s %s", name, err) return } + out.Protocol = "vxlan" + out.Connection = fmt.Sprintf("%s:%s", values[1], values[2]) } else { port.link = name } + + out.Device = port.link + cache.Output.Add(port.link, out) + w.out.Info("WorkerImpl.AddOutput %s %s", port.link, port.name) w.AddPhysical(bridge, port.vlan, port.link) } @@ -355,7 +374,10 @@ func (w *WorkerImpl) DelPhysical(bridge string, vlan int, output string) { func (w *WorkerImpl) DelOutput(bridge string, port *LinuxPort) { w.out.Info("WorkerImpl.DelOutput %s %s", port.link, port.name) + + cache.Output.Del(port.link) w.DelPhysical(bridge, port.vlan, port.link) + values := strings.SplitN(port.name, ":", 6) if values[0] == "gre" { link := &netlink.Gretap{