mirror of
https://github.com/vishvananda/netlink.git
synced 2025-10-05 15:56:54 +08:00
Add statistics to class attributes
This patch adds ClassStatistics, a struct that represents the stats of a class based on genric networking stats for netlink, to ClassAttrs. The parsers for rtattrs in type of TCA_STATS and TCA_STATS2 are introduced as well and the stats are appropriately parsed as a part of ClassAttrs struct. The practical tests for stats are not contained in this patch yet since it requires the actual packet sending/receiving in the random timing, which makes the tests complicated and flaky. Once we figure it out how to test them in the proper way, they shall be added. Signed-off-by: Taku Fukushima <taku@soracom.jp>
This commit is contained in:

committed by
Alessandro Boch

parent
aa0edbe0c9
commit
85aa3b74a4
57
class.go
57
class.go
@@ -9,14 +9,63 @@ type Class interface {
|
|||||||
Type() string
|
Type() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic networking statistics for netlink users.
|
||||||
|
// This file contains "gnet_" prefixed structs and relevant functions.
|
||||||
|
// See Documentation/networking/getn_stats.txt in Linux source code for more details.
|
||||||
|
|
||||||
|
// Ref: struct gnet_stats_basic { ... }
|
||||||
|
type GnetStatsBasic struct {
|
||||||
|
Bytes uint64 // number of seen bytes
|
||||||
|
Packets uint32 // number of seen packets
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref: struct gnet_stats_rate_est { ... }
|
||||||
|
type GnetStatsRateEst struct {
|
||||||
|
Bps uint32 // current byte rate
|
||||||
|
Pps uint32 // current packet rate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref: struct gnet_stats_rate_est64 { ... }
|
||||||
|
type GnetStatsRateEst64 struct {
|
||||||
|
Bps uint64 // current byte rate
|
||||||
|
Pps uint64 // current packet rate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref: struct gnet_stats_queue { ... }
|
||||||
|
type GnetStatsQueue struct {
|
||||||
|
Qlen uint32 // queue length
|
||||||
|
Backlog uint32 // backlog size of queue
|
||||||
|
Drops uint32 // number of dropped packets
|
||||||
|
Requeues uint32 // number of requues
|
||||||
|
Overlimits uint32 // number of enqueues over the limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics representaion based on generic networking statisticsfor netlink.
|
||||||
|
// See Documentation/networking/gen_stats.txt in Linux source code for more details.
|
||||||
|
type ClassStatistics struct {
|
||||||
|
Basic *GnetStatsBasic
|
||||||
|
Queue *GnetStatsQueue
|
||||||
|
RateEst *GnetStatsRateEst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a ClassStatistics struct which fields are all initialized by 0.
|
||||||
|
func NewClassStatistics() *ClassStatistics {
|
||||||
|
return &ClassStatistics{
|
||||||
|
Basic: &GnetStatsBasic{},
|
||||||
|
Queue: &GnetStatsQueue{},
|
||||||
|
RateEst: &GnetStatsRateEst{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ClassAttrs represents a netlink class. A filter is associated with a link,
|
// ClassAttrs represents a netlink class. A filter is associated with a link,
|
||||||
// has a handle and a parent. The root filter of a device should have a
|
// has a handle and a parent. The root filter of a device should have a
|
||||||
// parent == HANDLE_ROOT.
|
// parent == HANDLE_ROOT.
|
||||||
type ClassAttrs struct {
|
type ClassAttrs struct {
|
||||||
LinkIndex int
|
LinkIndex int
|
||||||
Handle uint32
|
Handle uint32
|
||||||
Parent uint32
|
Parent uint32
|
||||||
Leaf uint32
|
Leaf uint32
|
||||||
|
Statistics *ClassStatistics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q ClassAttrs) String() string {
|
func (q ClassAttrs) String() string {
|
||||||
|
@@ -1,13 +1,33 @@
|
|||||||
package netlink
|
package netlink
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink/nl"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Internal tc_stats representation in Go struct.
|
||||||
|
// This is for internal uses only to deserialize the payload of rtattr.
|
||||||
|
// After the deserialization, this should be converted into the canonical stats
|
||||||
|
// struct, ClassStatistics, in case of statistics of a class.
|
||||||
|
// Ref: struct tc_stats { ... }
|
||||||
|
type tcStats struct {
|
||||||
|
Bytes uint64 // Number of enqueued bytes
|
||||||
|
Packets uint32 // Number of enqueued packets
|
||||||
|
Drops uint32 // Packets dropped because of lack of resources
|
||||||
|
Overlimits uint32 // Number of throttle events when this flow goes out of allocated bandwidth
|
||||||
|
Bps uint32 // Current flow byte rate
|
||||||
|
Pps uint32 // Current flow packet rate
|
||||||
|
Qlen uint32
|
||||||
|
Backlog uint32
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: function is in here because it uses other linux functions
|
// NOTE: function is in here because it uses other linux functions
|
||||||
func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
|
func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
|
||||||
mtu := 1600
|
mtu := 1600
|
||||||
@@ -197,9 +217,10 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
base := ClassAttrs{
|
base := ClassAttrs{
|
||||||
LinkIndex: int(msg.Ifindex),
|
LinkIndex: int(msg.Ifindex),
|
||||||
Handle: msg.Handle,
|
Handle: msg.Handle,
|
||||||
Parent: msg.Parent,
|
Parent: msg.Parent,
|
||||||
|
Statistics: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
var class Class
|
var class Class
|
||||||
@@ -226,6 +247,17 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// For backward compatibility.
|
||||||
|
case nl.TCA_STATS:
|
||||||
|
base.Statistics, err = parseTcStats(attr.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case nl.TCA_STATS2:
|
||||||
|
base.Statistics, err = parseTcStats2(attr.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*class.Attrs() = base
|
*class.Attrs() = base
|
||||||
@@ -253,3 +285,61 @@ func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, erro
|
|||||||
}
|
}
|
||||||
return detailed, nil
|
return detailed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTcStats(data []byte) (*ClassStatistics, error) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
buf.Write(data)
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
tcStats := &tcStats{}
|
||||||
|
if err := binary.Read(buf, native, tcStats); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := NewClassStatistics()
|
||||||
|
stats.Basic.Bytes = tcStats.Bytes
|
||||||
|
stats.Basic.Packets = tcStats.Packets
|
||||||
|
stats.Queue.Qlen = tcStats.Qlen
|
||||||
|
stats.Queue.Backlog = tcStats.Backlog
|
||||||
|
stats.Queue.Drops = tcStats.Drops
|
||||||
|
stats.Queue.Overlimits = tcStats.Overlimits
|
||||||
|
stats.RateEst.Bps = tcStats.Bps
|
||||||
|
stats.RateEst.Pps = tcStats.Pps
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGnetStats(data []byte, gnetStats interface{}) error {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
buf.Write(data)
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
return binary.Read(buf, native, gnetStats)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTcStats2(data []byte) (*ClassStatistics, error) {
|
||||||
|
rtAttrs, err := nl.ParseRouteAttr(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stats := NewClassStatistics()
|
||||||
|
for _, datum := range rtAttrs {
|
||||||
|
switch datum.Attr.Type {
|
||||||
|
case nl.TCA_STATS_BASIC:
|
||||||
|
if err := parseGnetStats(datum.Value, stats.Basic); err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse ClassStatistics.Basic with: %v\n%s",
|
||||||
|
err, hex.Dump(datum.Value))
|
||||||
|
}
|
||||||
|
case nl.TCA_STATS_QUEUE:
|
||||||
|
if err := parseGnetStats(datum.Value, stats.Queue); err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse ClassStatistics.Queue with: %v\n%s",
|
||||||
|
err, hex.Dump(datum.Value))
|
||||||
|
}
|
||||||
|
case nl.TCA_STATS_RATE_EST:
|
||||||
|
if err := parseGnetStats(datum.Value, stats.RateEst); err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s",
|
||||||
|
err, hex.Dump(datum.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
package netlink
|
package netlink
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,6 +24,13 @@ func SafeQdiscList(link Link) ([]Qdisc, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testClassStats(this, that *ClassStatistics, t *testing.T) {
|
||||||
|
ok := reflect.DeepEqual(this, that)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("%#v is expected but it actually was %#v", that, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestClassAddDel(t *testing.T) {
|
func TestClassAddDel(t *testing.T) {
|
||||||
tearDown := setUpNetlinkTest(t)
|
tearDown := setUpNetlinkTest(t)
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
@@ -99,6 +107,8 @@ func TestClassAddDel(t *testing.T) {
|
|||||||
t.Fatal("Cbuffer doesn't match")
|
t.Fatal("Cbuffer doesn't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t)
|
||||||
|
|
||||||
qattrs := QdiscAttrs{
|
qattrs := QdiscAttrs{
|
||||||
LinkIndex: link.Attrs().Index,
|
LinkIndex: link.Attrs().Index,
|
||||||
Handle: MakeHandle(0x2, 0),
|
Handle: MakeHandle(0x2, 0),
|
||||||
@@ -250,6 +260,8 @@ func TestHtbClassAddHtbClassChangeDel(t *testing.T) {
|
|||||||
t.Fatal("Class is the wrong type")
|
t.Fatal("Class is the wrong type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t)
|
||||||
|
|
||||||
qattrs := QdiscAttrs{
|
qattrs := QdiscAttrs{
|
||||||
LinkIndex: link.Attrs().Index,
|
LinkIndex: link.Attrs().Index,
|
||||||
Handle: MakeHandle(0x2, 0),
|
Handle: MakeHandle(0x2, 0),
|
||||||
|
@@ -64,6 +64,15 @@ const (
|
|||||||
TCA_PRIO_MAX = TCA_PRIO_MQ
|
TCA_PRIO_MAX = TCA_PRIO_MQ
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TCA_STATS_UNSPEC = iota
|
||||||
|
TCA_STATS_BASIC
|
||||||
|
TCA_STATS_RATE_EST
|
||||||
|
TCA_STATS_QUEUE
|
||||||
|
TCA_STATS_APP
|
||||||
|
TCA_STATS_MAX = TCA_STATS_APP
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SizeofTcMsg = 0x14
|
SizeofTcMsg = 0x14
|
||||||
SizeofTcActionMsg = 0x04
|
SizeofTcActionMsg = 0x04
|
||||||
|
Reference in New Issue
Block a user