123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- package netlink
- import (
- "bytes"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "fmt"
- "syscall"
- "github.com/vishvananda/netlink/nl"
- "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
- }
- // NewHtbClass NOTE: function is in here because it uses other linux functions
- func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
- mtu := 1600
- rate := cattrs.Rate / 8
- ceil := cattrs.Ceil / 8
- buffer := cattrs.Buffer
- cbuffer := cattrs.Cbuffer
- if ceil == 0 {
- ceil = rate
- }
- if buffer == 0 {
- buffer = uint32(float64(rate)/Hz() + float64(mtu))
- }
- buffer = uint32(Xmittime(rate, buffer))
- if cbuffer == 0 {
- cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
- }
- cbuffer = uint32(Xmittime(ceil, cbuffer))
- return &HtbClass{
- ClassAttrs: attrs,
- Rate: rate,
- Ceil: ceil,
- Buffer: buffer,
- Cbuffer: cbuffer,
- Quantum: 10,
- Level: 0,
- Prio: 0,
- }
- }
- // ClassDel will delete a class from the system.
- // Equivalent to: `tc class del $class`
- func ClassDel(class Class) error {
- return pkgHandle.ClassDel(class)
- }
- // ClassDel will delete a class from the system.
- // Equivalent to: `tc class del $class`
- func (h *Handle) ClassDel(class Class) error {
- return h.classModify(unix.RTM_DELTCLASS, 0, class)
- }
- // ClassChange will change a class in place
- // Equivalent to: `tc class change $class`
- // The parent and handle MUST NOT be changed.
- func ClassChange(class Class) error {
- return pkgHandle.ClassChange(class)
- }
- // ClassChange will change a class in place
- // Equivalent to: `tc class change $class`
- // The parent and handle MUST NOT be changed.
- func (h *Handle) ClassChange(class Class) error {
- return h.classModify(unix.RTM_NEWTCLASS, 0, class)
- }
- // ClassReplace will replace a class to the system.
- // quivalent to: `tc class replace $class`
- // The handle MAY be changed.
- // If a class already exist with this parent/handle pair, the class is changed.
- // If a class does not already exist with this parent/handle, a new class is created.
- func ClassReplace(class Class) error {
- return pkgHandle.ClassReplace(class)
- }
- // ClassReplace will replace a class to the system.
- // quivalent to: `tc class replace $class`
- // The handle MAY be changed.
- // If a class already exist with this parent/handle pair, the class is changed.
- // If a class does not already exist with this parent/handle, a new class is created.
- func (h *Handle) ClassReplace(class Class) error {
- return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class)
- }
- // ClassAdd will add a class to the system.
- // Equivalent to: `tc class add $class`
- func ClassAdd(class Class) error {
- return pkgHandle.ClassAdd(class)
- }
- // ClassAdd will add a class to the system.
- // Equivalent to: `tc class add $class`
- func (h *Handle) ClassAdd(class Class) error {
- return h.classModify(
- unix.RTM_NEWTCLASS,
- unix.NLM_F_CREATE|unix.NLM_F_EXCL,
- class,
- )
- }
- func (h *Handle) classModify(cmd, flags int, class Class) error {
- req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
- base := class.Attrs()
- msg := &nl.TcMsg{
- Family: nl.FAMILY_ALL,
- Ifindex: int32(base.LinkIndex),
- Handle: base.Handle,
- Parent: base.Parent,
- }
- req.AddData(msg)
- if cmd != unix.RTM_DELTCLASS {
- if err := classPayload(req, class); err != nil {
- return err
- }
- }
- _, err := req.Execute(unix.NETLINK_ROUTE, 0)
- return err
- }
- func classPayload(req *nl.NetlinkRequest, class Class) error {
- req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
- options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
- switch class.Type() {
- case "htb":
- htb := class.(*HtbClass)
- opt := nl.TcHtbCopt{}
- opt.Buffer = htb.Buffer
- opt.Cbuffer = htb.Cbuffer
- opt.Quantum = htb.Quantum
- opt.Level = htb.Level
- opt.Prio = htb.Prio
- // TODO: Handle Debug properly. For now default to 0
- /* Calculate {R,C}Tab and set Rate and Ceil */
- cellLog := -1
- ccellLog := -1
- linklayer := nl.LINKLAYER_ETHERNET
- mtu := 1600
- var rtab [256]uint32
- var ctab [256]uint32
- tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
- if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 {
- return errors.New("HTB: failed to calculate rate table")
- }
- opt.Rate = tcrate
- tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
- if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 {
- return errors.New("HTB: failed to calculate ceil rate table")
- }
- opt.Ceil = tcceil
- options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize())
- options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab))
- options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab))
- case "hfsc":
- hfsc := class.(*HfscClass)
- opt := nl.HfscCopt{}
- opt.Rsc.Set(hfsc.Rsc.Attrs())
- opt.Fsc.Set(hfsc.Fsc.Attrs())
- opt.Usc.Set(hfsc.Usc.Attrs())
- options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
- options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
- options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
- }
- req.AddData(options)
- return nil
- }
- // ClassList gets a list of classes in the system.
- // Equivalent to: `tc class show`.
- // Generally returns nothing if link and parent are not specified.
- func ClassList(link Link, parent uint32) ([]Class, error) {
- return pkgHandle.ClassList(link, parent)
- }
- // ClassList gets a list of classes in the system.
- // Equivalent to: `tc class show`.
- // Generally returns nothing if link and parent are not specified.
- func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
- req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
- msg := &nl.TcMsg{
- Family: nl.FAMILY_ALL,
- Parent: parent,
- }
- if link != nil {
- base := link.Attrs()
- h.ensureIndex(base)
- msg.Ifindex = int32(base.Index)
- }
- req.AddData(msg)
- msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
- if err != nil {
- return nil, err
- }
- var res []Class
- for _, m := range msgs {
- msg := nl.DeserializeTcMsg(m)
- attrs, err := nl.ParseRouteAttr(m[msg.Len():])
- if err != nil {
- return nil, err
- }
- base := ClassAttrs{
- LinkIndex: int(msg.Ifindex),
- Handle: msg.Handle,
- Parent: msg.Parent,
- Statistics: nil,
- }
- var class Class
- classType := ""
- for _, attr := range attrs {
- switch attr.Attr.Type {
- case nl.TCA_KIND:
- classType = string(attr.Value[:len(attr.Value)-1])
- switch classType {
- case "htb":
- class = &HtbClass{}
- case "hfsc":
- class = &HfscClass{}
- default:
- class = &GenericClass{ClassType: classType}
- }
- case nl.TCA_OPTIONS:
- switch classType {
- case "htb":
- data, err := nl.ParseRouteAttr(attr.Value)
- if err != nil {
- return nil, err
- }
- _, err = parseHtbClassData(class, data)
- if err != nil {
- return nil, err
- }
- case "hfsc":
- data, err := nl.ParseRouteAttr(attr.Value)
- if err != nil {
- return nil, err
- }
- _, err = parseHfscClassData(class, data)
- if err != nil {
- 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
- res = append(res, class)
- }
- return res, nil
- }
- func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
- htb := class.(*HtbClass)
- detailed := false
- for _, datum := range data {
- switch datum.Attr.Type {
- case nl.TCA_HTB_PARMS:
- opt := nl.DeserializeTcHtbCopt(datum.Value)
- htb.Rate = uint64(opt.Rate.Rate)
- htb.Ceil = uint64(opt.Ceil.Rate)
- htb.Buffer = opt.Buffer
- htb.Cbuffer = opt.Cbuffer
- htb.Quantum = opt.Quantum
- htb.Level = opt.Level
- htb.Prio = opt.Prio
- }
- }
- return detailed, nil
- }
- func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
- hfsc := class.(*HfscClass)
- detailed := false
- for _, datum := range data {
- m1, d, m2 := nl.DeserializeHfscCurve(datum.Value).Attrs()
- switch datum.Attr.Type {
- case nl.TCA_HFSC_RSC:
- hfsc.Rsc = ServiceCurve{m1: m1, d: d, m2: m2}
- case nl.TCA_HFSC_FSC:
- hfsc.Fsc = ServiceCurve{m1: m1, d: d, m2: m2}
- case nl.TCA_HFSC_USC:
- hfsc.Usc = ServiceCurve{m1: m1, d: d, m2: m2}
- }
- }
- 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
- }
|