github.com/vishvananda/netlink@v1.3.1/class_linux.go (about)

     1  package netlink
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"errors"
     8  	"fmt"
     9  	"syscall"
    10  
    11  	"github.com/vishvananda/netlink/nl"
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  // Internal tc_stats representation in Go struct.
    16  // This is for internal uses only to deserialize the payload of rtattr.
    17  // After the deserialization, this should be converted into the canonical stats
    18  // struct, ClassStatistics, in case of statistics of a class.
    19  // Ref: struct tc_stats { ... }
    20  type tcStats struct {
    21  	Bytes      uint64 // Number of enqueued bytes
    22  	Packets    uint32 // Number of enqueued packets
    23  	Drops      uint32 // Packets dropped because of lack of resources
    24  	Overlimits uint32 // Number of throttle events when this flow goes out of allocated bandwidth
    25  	Bps        uint32 // Current flow byte rate
    26  	Pps        uint32 // Current flow packet rate
    27  	Qlen       uint32
    28  	Backlog    uint32
    29  }
    30  
    31  // NewHtbClass NOTE: function is in here because it uses other linux functions
    32  func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
    33  	mtu := 1600
    34  	rate := cattrs.Rate / 8
    35  	ceil := cattrs.Ceil / 8
    36  	buffer := cattrs.Buffer
    37  	cbuffer := cattrs.Cbuffer
    38  
    39  	if ceil == 0 {
    40  		ceil = rate
    41  	}
    42  
    43  	if buffer == 0 {
    44  		buffer = uint32(float64(rate)/Hz() + float64(mtu))
    45  	}
    46  	buffer = Xmittime(rate, buffer)
    47  
    48  	if cbuffer == 0 {
    49  		cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
    50  	}
    51  	cbuffer = Xmittime(ceil, cbuffer)
    52  
    53  	return &HtbClass{
    54  		ClassAttrs: attrs,
    55  		Rate:       rate,
    56  		Ceil:       ceil,
    57  		Buffer:     buffer,
    58  		Cbuffer:    cbuffer,
    59  		Level:      0,
    60  		Prio:       cattrs.Prio,
    61  		Quantum:    cattrs.Quantum,
    62  	}
    63  }
    64  
    65  // ClassDel will delete a class from the system.
    66  // Equivalent to: `tc class del $class`
    67  func ClassDel(class Class) error {
    68  	return pkgHandle.ClassDel(class)
    69  }
    70  
    71  // ClassDel will delete a class from the system.
    72  // Equivalent to: `tc class del $class`
    73  func (h *Handle) ClassDel(class Class) error {
    74  	return h.classModify(unix.RTM_DELTCLASS, 0, class)
    75  }
    76  
    77  // ClassChange will change a class in place
    78  // Equivalent to: `tc class change $class`
    79  // The parent and handle MUST NOT be changed.
    80  func ClassChange(class Class) error {
    81  	return pkgHandle.ClassChange(class)
    82  }
    83  
    84  // ClassChange will change a class in place
    85  // Equivalent to: `tc class change $class`
    86  // The parent and handle MUST NOT be changed.
    87  func (h *Handle) ClassChange(class Class) error {
    88  	return h.classModify(unix.RTM_NEWTCLASS, 0, class)
    89  }
    90  
    91  // ClassReplace will replace a class to the system.
    92  // quivalent to: `tc class replace $class`
    93  // The handle MAY be changed.
    94  // If a class already exist with this parent/handle pair, the class is changed.
    95  // If a class does not already exist with this parent/handle, a new class is created.
    96  func ClassReplace(class Class) error {
    97  	return pkgHandle.ClassReplace(class)
    98  }
    99  
   100  // ClassReplace will replace a class to the system.
   101  // quivalent to: `tc class replace $class`
   102  // The handle MAY be changed.
   103  // If a class already exist with this parent/handle pair, the class is changed.
   104  // If a class does not already exist with this parent/handle, a new class is created.
   105  func (h *Handle) ClassReplace(class Class) error {
   106  	return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class)
   107  }
   108  
   109  // ClassAdd will add a class to the system.
   110  // Equivalent to: `tc class add $class`
   111  func ClassAdd(class Class) error {
   112  	return pkgHandle.ClassAdd(class)
   113  }
   114  
   115  // ClassAdd will add a class to the system.
   116  // Equivalent to: `tc class add $class`
   117  func (h *Handle) ClassAdd(class Class) error {
   118  	return h.classModify(
   119  		unix.RTM_NEWTCLASS,
   120  		unix.NLM_F_CREATE|unix.NLM_F_EXCL,
   121  		class,
   122  	)
   123  }
   124  
   125  func (h *Handle) classModify(cmd, flags int, class Class) error {
   126  	req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
   127  	base := class.Attrs()
   128  	msg := &nl.TcMsg{
   129  		Family:  nl.FAMILY_ALL,
   130  		Ifindex: int32(base.LinkIndex),
   131  		Handle:  base.Handle,
   132  		Parent:  base.Parent,
   133  	}
   134  	req.AddData(msg)
   135  
   136  	if cmd != unix.RTM_DELTCLASS {
   137  		if err := classPayload(req, class); err != nil {
   138  			return err
   139  		}
   140  	}
   141  	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
   142  	return err
   143  }
   144  
   145  func classPayload(req *nl.NetlinkRequest, class Class) error {
   146  	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
   147  
   148  	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
   149  	switch class.Type() {
   150  	case "htb":
   151  		htb := class.(*HtbClass)
   152  		opt := nl.TcHtbCopt{}
   153  		opt.Buffer = htb.Buffer
   154  		opt.Cbuffer = htb.Cbuffer
   155  		opt.Quantum = htb.Quantum
   156  		opt.Level = htb.Level
   157  		opt.Prio = htb.Prio
   158  		// TODO: Handle Debug properly. For now default to 0
   159  		/* Calculate {R,C}Tab and set Rate and Ceil */
   160  		cellLog := -1
   161  		ccellLog := -1
   162  		linklayer := nl.LINKLAYER_ETHERNET
   163  		mtu := 1600
   164  		var rtab [256]uint32
   165  		var ctab [256]uint32
   166  		tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
   167  		if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 {
   168  			return errors.New("HTB: failed to calculate rate table")
   169  		}
   170  		opt.Rate = tcrate
   171  		tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
   172  		if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 {
   173  			return errors.New("HTB: failed to calculate ceil rate table")
   174  		}
   175  		opt.Ceil = tcceil
   176  		options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize())
   177  		options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab))
   178  		options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab))
   179  		if htb.Rate >= uint64(1<<32) {
   180  			options.AddRtAttr(nl.TCA_HTB_RATE64, nl.Uint64Attr(htb.Rate))
   181  		}
   182  		if htb.Ceil >= uint64(1<<32) {
   183  			options.AddRtAttr(nl.TCA_HTB_CEIL64, nl.Uint64Attr(htb.Ceil))
   184  		}
   185  	case "hfsc":
   186  		hfsc := class.(*HfscClass)
   187  		opt := nl.HfscCopt{}
   188  		rm1, rd, rm2 := hfsc.Rsc.Attrs()
   189  		opt.Rsc.Set(rm1/8, rd, rm2/8)
   190  		fm1, fd, fm2 := hfsc.Fsc.Attrs()
   191  		opt.Fsc.Set(fm1/8, fd, fm2/8)
   192  		um1, ud, um2 := hfsc.Usc.Attrs()
   193  		opt.Usc.Set(um1/8, ud, um2/8)
   194  		options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
   195  		options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
   196  		options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
   197  	}
   198  	req.AddData(options)
   199  	return nil
   200  }
   201  
   202  // ClassList gets a list of classes in the system.
   203  // Equivalent to: `tc class show`.
   204  //
   205  // Generally returns nothing if link and parent are not specified.
   206  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   207  // or incomplete.
   208  func ClassList(link Link, parent uint32) ([]Class, error) {
   209  	return pkgHandle.ClassList(link, parent)
   210  }
   211  
   212  // ClassList gets a list of classes in the system.
   213  // Equivalent to: `tc class show`.
   214  //
   215  // Generally returns nothing if link and parent are not specified.
   216  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   217  // or incomplete.
   218  func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
   219  	req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
   220  	msg := &nl.TcMsg{
   221  		Family: nl.FAMILY_ALL,
   222  		Parent: parent,
   223  	}
   224  	if link != nil {
   225  		base := link.Attrs()
   226  		h.ensureIndex(base)
   227  		msg.Ifindex = int32(base.Index)
   228  	}
   229  	req.AddData(msg)
   230  
   231  	msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
   232  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   233  		return nil, executeErr
   234  	}
   235  
   236  	var res []Class
   237  	for _, m := range msgs {
   238  		msg := nl.DeserializeTcMsg(m)
   239  
   240  		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  
   245  		base := ClassAttrs{
   246  			LinkIndex:  int(msg.Ifindex),
   247  			Handle:     msg.Handle,
   248  			Parent:     msg.Parent,
   249  			Statistics: nil,
   250  		}
   251  
   252  		var class Class
   253  		classType := ""
   254  		for _, attr := range attrs {
   255  			switch attr.Attr.Type {
   256  			case nl.TCA_KIND:
   257  				classType = string(attr.Value[:len(attr.Value)-1])
   258  				switch classType {
   259  				case "htb":
   260  					class = &HtbClass{}
   261  				case "hfsc":
   262  					class = &HfscClass{}
   263  				default:
   264  					class = &GenericClass{ClassType: classType}
   265  				}
   266  			case nl.TCA_OPTIONS:
   267  				switch classType {
   268  				case "htb":
   269  					data, err := nl.ParseRouteAttr(attr.Value)
   270  					if err != nil {
   271  						return nil, err
   272  					}
   273  					_, err = parseHtbClassData(class, data)
   274  					if err != nil {
   275  						return nil, err
   276  					}
   277  				case "hfsc":
   278  					data, err := nl.ParseRouteAttr(attr.Value)
   279  					if err != nil {
   280  						return nil, err
   281  					}
   282  					_, err = parseHfscClassData(class, data)
   283  					if err != nil {
   284  						return nil, err
   285  					}
   286  				}
   287  			// For backward compatibility.
   288  			case nl.TCA_STATS:
   289  				base.Statistics, err = parseTcStats(attr.Value)
   290  				if err != nil {
   291  					return nil, err
   292  				}
   293  			case nl.TCA_STATS2:
   294  				base.Statistics, err = parseTcStats2(attr.Value)
   295  				if err != nil {
   296  					return nil, err
   297  				}
   298  			}
   299  		}
   300  		*class.Attrs() = base
   301  		res = append(res, class)
   302  	}
   303  
   304  	return res, executeErr
   305  }
   306  
   307  func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
   308  	htb := class.(*HtbClass)
   309  	detailed := false
   310  	for _, datum := range data {
   311  		switch datum.Attr.Type {
   312  		case nl.TCA_HTB_PARMS:
   313  			opt := nl.DeserializeTcHtbCopt(datum.Value)
   314  			htb.Rate = uint64(opt.Rate.Rate)
   315  			htb.Ceil = uint64(opt.Ceil.Rate)
   316  			htb.Buffer = opt.Buffer
   317  			htb.Cbuffer = opt.Cbuffer
   318  			htb.Quantum = opt.Quantum
   319  			htb.Level = opt.Level
   320  			htb.Prio = opt.Prio
   321  		case nl.TCA_HTB_RATE64:
   322  			htb.Rate = native.Uint64(datum.Value[0:8])
   323  		case nl.TCA_HTB_CEIL64:
   324  			htb.Ceil = native.Uint64(datum.Value[0:8])
   325  		}
   326  	}
   327  	return detailed, nil
   328  }
   329  
   330  func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
   331  	hfsc := class.(*HfscClass)
   332  	detailed := false
   333  	for _, datum := range data {
   334  		m1, d, m2 := nl.DeserializeHfscCurve(datum.Value).Attrs()
   335  		switch datum.Attr.Type {
   336  		case nl.TCA_HFSC_RSC:
   337  			hfsc.Rsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
   338  		case nl.TCA_HFSC_FSC:
   339  			hfsc.Fsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
   340  		case nl.TCA_HFSC_USC:
   341  			hfsc.Usc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
   342  		}
   343  	}
   344  	return detailed, nil
   345  }
   346  
   347  func parseTcStats(data []byte) (*ClassStatistics, error) {
   348  	buf := &bytes.Buffer{}
   349  	buf.Write(data)
   350  	tcStats := &tcStats{}
   351  	if err := binary.Read(buf, native, tcStats); err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	stats := NewClassStatistics()
   356  	stats.Basic.Bytes = tcStats.Bytes
   357  	stats.Basic.Packets = tcStats.Packets
   358  	stats.Queue.Qlen = tcStats.Qlen
   359  	stats.Queue.Backlog = tcStats.Backlog
   360  	stats.Queue.Drops = tcStats.Drops
   361  	stats.Queue.Overlimits = tcStats.Overlimits
   362  	stats.RateEst.Bps = tcStats.Bps
   363  	stats.RateEst.Pps = tcStats.Pps
   364  
   365  	return stats, nil
   366  }
   367  
   368  func parseGnetStats(data []byte, gnetStats interface{}) error {
   369  	buf := &bytes.Buffer{}
   370  	buf.Write(data)
   371  	return binary.Read(buf, native, gnetStats)
   372  }
   373  
   374  func parseTcStats2(data []byte) (*ClassStatistics, error) {
   375  	rtAttrs, err := nl.ParseRouteAttr(data)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  	stats := NewClassStatistics()
   380  	for _, datum := range rtAttrs {
   381  		switch datum.Attr.Type {
   382  		case nl.TCA_STATS_BASIC:
   383  			if err := parseGnetStats(datum.Value, stats.Basic); err != nil {
   384  				return nil, fmt.Errorf("Failed to parse ClassStatistics.Basic with: %v\n%s",
   385  					err, hex.Dump(datum.Value))
   386  			}
   387  		case nl.TCA_STATS_QUEUE:
   388  			if err := parseGnetStats(datum.Value, stats.Queue); err != nil {
   389  				return nil, fmt.Errorf("Failed to parse ClassStatistics.Queue with: %v\n%s",
   390  					err, hex.Dump(datum.Value))
   391  			}
   392  		case nl.TCA_STATS_RATE_EST:
   393  			if err := parseGnetStats(datum.Value, stats.RateEst); err != nil {
   394  				return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s",
   395  					err, hex.Dump(datum.Value))
   396  			}
   397  		case nl.TCA_STATS_BASIC_HW:
   398  			if err := parseGnetStats(datum.Value, stats.BasicHw); err != nil {
   399  				return nil, fmt.Errorf("Failed to parse ClassStatistics.BasicHw with: %v\n%s",
   400  					err, hex.Dump(datum.Value))
   401  			}
   402  		}
   403  	}
   404  
   405  	return stats, nil
   406  }