From 0ced8385386ae7d3a2bdd4340d22da6a137f446c Mon Sep 17 00:00:00 2001 From: "liuhao.0912" Date: Thu, 27 Jul 2023 14:37:55 +0800 Subject: [PATCH] enable rule statistic and time statistic for flower actions --- class.go | 2 ++ class_linux.go | 5 ++++ filter.go | 25 +++++++++++++++---- filter_linux.go | 40 ++++++++++++++++++++++++++++++ filter_test.go | 18 ++++++++++++++ nl/tc_linux.go | 66 ++++++++++++++++++++++++++++++++----------------- 6 files changed, 128 insertions(+), 28 deletions(-) diff --git a/class.go b/class.go index 10ceffe..e686f67 100644 --- a/class.go +++ b/class.go @@ -47,6 +47,7 @@ type ClassStatistics struct { Basic *GnetStatsBasic Queue *GnetStatsQueue RateEst *GnetStatsRateEst + BasicHw *GnetStatsBasic // Hardward statistics added in kernel 4.20 } // NewClassStatistics Construct a ClassStatistics struct which fields are all initialized by 0. @@ -55,6 +56,7 @@ func NewClassStatistics() *ClassStatistics { Basic: &GnetStatsBasic{}, Queue: &GnetStatsQueue{}, RateEst: &GnetStatsRateEst{}, + BasicHw: &GnetStatsBasic{}, } } diff --git a/class_linux.go b/class_linux.go index 6f542ba..a82eb09 100644 --- a/class_linux.go +++ b/class_linux.go @@ -388,6 +388,11 @@ func parseTcStats2(data []byte) (*ClassStatistics, error) { return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s", err, hex.Dump(datum.Value)) } + case nl.TCA_STATS_BASIC_HW: + if err := parseGnetStats(datum.Value, stats.BasicHw); err != nil { + return nil, fmt.Errorf("Failed to parse ClassStatistics.BasicHw with: %v\n%s", + err, hex.Dump(datum.Value)) + } } } diff --git a/filter.go b/filter.go index 08e66ae..84e1ca7 100644 --- a/filter.go +++ b/filter.go @@ -118,17 +118,32 @@ func (a TcPolAct) String() string { } type ActionAttrs struct { - Index int - Capab int - Action TcAct - Refcnt int - Bindcnt int + Index int + Capab int + Action TcAct + Refcnt int + Bindcnt int + Statistics *ActionStatistic + Timestamp *ActionTimestamp } func (q ActionAttrs) String() string { return fmt.Sprintf("{Index: %d, Capab: %x, Action: %s, Refcnt: %d, Bindcnt: %d}", q.Index, q.Capab, q.Action.String(), q.Refcnt, q.Bindcnt) } +type ActionTimestamp struct { + Installed uint64 + LastUsed uint64 + Expires uint64 + FirstUsed uint64 +} + +func (t ActionTimestamp) String() string { + return fmt.Sprintf("Installed %d LastUsed %d Expires %d FirstUsed %d", t.Installed, t.LastUsed, t.Expires, t.FirstUsed) +} + +type ActionStatistic ClassStatistics + // Action represents an action in any supported filter. type Action interface { Attrs() *ActionAttrs diff --git a/filter_linux.go b/filter_linux.go index ae978b3..536e268 100644 --- a/filter_linux.go +++ b/filter_linux.go @@ -528,6 +528,14 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) { attrs.Bindcnt = int(tcgen.Bindcnt) } +func toTimeStamp(tcf *nl.Tcf) *ActionTimestamp { + return &ActionTimestamp{ + Installed: tcf.Install, + LastUsed: tcf.LastUse, + Expires: tcf.Expires, + FirstUsed: tcf.FirstUse} +} + func encodePolice(attr *nl.RtAttr, action *PoliceAction) error { var rtab [256]uint32 var ptab [256]uint32 @@ -748,6 +756,8 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { for _, table := range tables { var action Action var actionType string + var actionnStatistic *ActionStatistic + var actionTimestamp *ActionTimestamp aattrs, err := nl.ParseRouteAttr(table.Value) if err != nil { return nil, err @@ -795,7 +805,11 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { toAttrs(&mirred.TcGen, action.Attrs()) action.(*MirredAction).Ifindex = int(mirred.Ifindex) action.(*MirredAction).MirredAction = MirredAct(mirred.Eaction) + case nl.TCA_MIRRED_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } + case "tunnel_key": switch adatum.Attr.Type { case nl.TCA_TUNNEL_KEY_PARMS: @@ -811,6 +825,9 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { action.(*TunnelKeyAction).DstAddr = adatum.Value[:] case nl.TCA_TUNNEL_KEY_ENC_DST_PORT: action.(*TunnelKeyAction).DestPort = ntohs(adatum.Value) + case nl.TCA_TUNNEL_KEY_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } case "skbedit": switch adatum.Attr.Type { @@ -833,6 +850,9 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { case nl.TCA_SKBEDIT_QUEUE_MAPPING: mapping := native.Uint16(adatum.Value[0:2]) action.(*SkbEditAction).QueueMapping = &mapping + case nl.TCA_SKBEDIT_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } case "bpf": switch adatum.Attr.Type { @@ -843,6 +863,9 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { action.(*BpfAction).Fd = int(native.Uint32(adatum.Value[0:4])) case nl.TCA_ACT_BPF_NAME: action.(*BpfAction).Name = string(adatum.Value[:len(adatum.Value)-1]) + case nl.TCA_ACT_BPF_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } case "connmark": switch adatum.Attr.Type { @@ -851,6 +874,9 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { action.(*ConnmarkAction).ActionAttrs = ActionAttrs{} toAttrs(&connmark.TcGen, action.Attrs()) action.(*ConnmarkAction).Zone = connmark.Zone + case nl.TCA_CONNMARK_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } case "csum": switch adatum.Attr.Type { @@ -859,6 +885,9 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { action.(*CsumAction).ActionAttrs = ActionAttrs{} toAttrs(&csum.TcGen, action.Attrs()) action.(*CsumAction).UpdateFlags = CsumUpdateFlags(csum.UpdateFlags) + case nl.TCA_CSUM_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } case "gact": switch adatum.Attr.Type { @@ -868,13 +897,24 @@ func parseActions(tables []syscall.NetlinkRouteAttr) ([]Action, error) { if action.Attrs().Action.String() == "goto" { action.(*GenericAction).Chain = TC_ACT_EXT_VAL_MASK & gen.Action } + case nl.TCA_GACT_TM: + tcTs := nl.DeserializeTcf(adatum.Value) + actionTimestamp = toTimeStamp(tcTs) } case "police": parsePolice(adatum, action.(*PoliceAction)) } } + case nl.TCA_ACT_STATS: + s, err := parseTcStats2(aattr.Value) + if err != nil { + return nil, err + } + actionnStatistic = (*ActionStatistic)(s) } } + action.Attrs().Statistics = actionnStatistic + action.Attrs().Timestamp = actionTimestamp actions = append(actions, action) } return actions, nil diff --git a/filter_test.go b/filter_test.go index 5996512..e8f8f3c 100644 --- a/filter_test.go +++ b/filter_test.go @@ -7,6 +7,7 @@ import ( "net" "reflect" "testing" + "time" "github.com/vishvananda/netlink/nl" "golang.org/x/sys/unix" @@ -1815,6 +1816,7 @@ func TestFilterFlowerAddDel(t *testing.T) { t.Fatal(err) } + time.Sleep(time.Second) filters, err := FilterList(link, MakeHandle(0xffff, 0)) if err != nil { t.Fatal(err) @@ -1881,6 +1883,14 @@ func TestFilterFlowerAddDel(t *testing.T) { t.Fatal("Mirred action isn't TC_ACT_STOLEN") } + if mia.Timestamp == nil || mia.Timestamp.Installed == 0 { + t.Fatal("Incorrect mirred action timestamp") + } + + if mia.Statistics == nil { + t.Fatal("Incorrect mirred action stats") + } + ga, ok := flower.Actions[1].(*GenericAction) if !ok { t.Fatal("Unable to find generic action") @@ -1890,6 +1900,14 @@ func TestFilterFlowerAddDel(t *testing.T) { t.Fatal("Generic action isn't TC_ACT_GOTO_CHAIN") } + if ga.Timestamp == nil || ga.Timestamp.Installed == 0 { + t.Fatal("Incorrect generic action timestamp") + } + + if ga.Statistics == nil { + t.Fatal("Incorrect generic action stats") + } + if err := FilterDel(filter); err != nil { t.Fatal(err) } diff --git a/nl/tc_linux.go b/nl/tc_linux.go index c98173e..e9ce190 100644 --- a/nl/tc_linux.go +++ b/nl/tc_linux.go @@ -89,7 +89,11 @@ const ( TCA_STATS_RATE_EST TCA_STATS_QUEUE TCA_STATS_APP - TCA_STATS_MAX = TCA_STATS_APP + TCA_STATS_RATE_EST64 + TCA_STATS_PAD + TCA_STATS_BASIC_HW + TCA_STATS_PKT64 + TCA_STATS_MAX = TCA_STATS_PKT64 ) const ( @@ -150,6 +154,18 @@ func (x *TcMsg) Serialize() []byte { return (*(*[SizeofTcMsg]byte)(unsafe.Pointer(x)))[:] } +type Tcf struct { + Install uint64 + LastUse uint64 + Expires uint64 + FirstUse uint64 +} + +func DeserializeTcf(b []byte) *Tcf { + const size = int(unsafe.Sizeof(Tcf{})) + return (*Tcf)(unsafe.Pointer(&b[0:size][0])) +} + // struct tcamsg { // unsigned char tca_family; // unsigned char tca__pad1; @@ -1073,14 +1089,14 @@ func (x *TcSfqQopt) Serialize() []byte { return (*(*[SizeofTcSfqQopt]byte)(unsafe.Pointer(x)))[:] } -// struct tc_sfqred_stats { -// __u32 prob_drop; /* Early drops, below max threshold */ -// __u32 forced_drop; /* Early drops, after max threshold */ -// __u32 prob_mark; /* Marked packets, below max threshold */ -// __u32 forced_mark; /* Marked packets, after max threshold */ -// __u32 prob_mark_head; /* Marked packets, below max threshold */ -// __u32 forced_mark_head;/* Marked packets, after max threshold */ -// }; +// struct tc_sfqred_stats { +// __u32 prob_drop; /* Early drops, below max threshold */ +// __u32 forced_drop; /* Early drops, after max threshold */ +// __u32 prob_mark; /* Marked packets, below max threshold */ +// __u32 forced_mark; /* Marked packets, after max threshold */ +// __u32 prob_mark_head; /* Marked packets, below max threshold */ +// __u32 forced_mark_head;/* Marked packets, after max threshold */ +// }; type TcSfqRedStats struct { ProbDrop uint32 ForcedDrop uint32 @@ -1102,22 +1118,26 @@ func (x *TcSfqRedStats) Serialize() []byte { return (*(*[SizeofTcSfqRedStats]byte)(unsafe.Pointer(x)))[:] } -// struct tc_sfq_qopt_v1 { -// struct tc_sfq_qopt v0; -// unsigned int depth; /* max number of packets per flow */ -// unsigned int headdrop; +// struct tc_sfq_qopt_v1 { +// struct tc_sfq_qopt v0; +// unsigned int depth; /* max number of packets per flow */ +// unsigned int headdrop; +// // /* SFQRED parameters */ -// __u32 limit; /* HARD maximal flow queue length (bytes) */ -// __u32 qth_min; /* Min average length threshold (bytes) */ -// __u32 qth_max; /* Max average length threshold (bytes) */ -// unsigned char Wlog; /* log(W) */ -// unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ -// unsigned char Scell_log; /* cell size for idle damping */ -// unsigned char flags; -// __u32 max_P; /* probability, high resolution */ +// +// __u32 limit; /* HARD maximal flow queue length (bytes) */ +// __u32 qth_min; /* Min average length threshold (bytes) */ +// __u32 qth_max; /* Max average length threshold (bytes) */ +// unsigned char Wlog; /* log(W) */ +// unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ +// unsigned char Scell_log; /* cell size for idle damping */ +// unsigned char flags; +// __u32 max_P; /* probability, high resolution */ +// // /* SFQRED stats */ -// struct tc_sfqred_stats stats; -// }; +// +// struct tc_sfqred_stats stats; +// }; type TcSfqQoptV1 struct { TcSfqQopt Depth uint32