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

     1  package netlink
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  	"syscall"
    11  
    12  	"github.com/vishvananda/netlink/nl"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  // NOTE function is here because it uses other linux functions
    17  func NewNetem(attrs QdiscAttrs, nattrs NetemQdiscAttrs) *Netem {
    18  	var limit uint32 = 1000
    19  	var lossCorr, delayCorr, duplicateCorr uint32
    20  	var reorderProb, reorderCorr uint32
    21  	var corruptProb, corruptCorr uint32
    22  	var rate64 uint64
    23  
    24  	latency := nattrs.Latency
    25  	loss := Percentage2u32(nattrs.Loss)
    26  	gap := nattrs.Gap
    27  	duplicate := Percentage2u32(nattrs.Duplicate)
    28  	jitter := nattrs.Jitter
    29  
    30  	// Correlation
    31  	if latency > 0 && jitter > 0 {
    32  		delayCorr = Percentage2u32(nattrs.DelayCorr)
    33  	}
    34  	if loss > 0 {
    35  		lossCorr = Percentage2u32(nattrs.LossCorr)
    36  	}
    37  	if duplicate > 0 {
    38  		duplicateCorr = Percentage2u32(nattrs.DuplicateCorr)
    39  	}
    40  	// FIXME should validate values(like loss/duplicate are percentages...)
    41  	latency = time2Tick(latency)
    42  
    43  	if nattrs.Limit != 0 {
    44  		limit = nattrs.Limit
    45  	}
    46  	// Jitter is only value if latency is > 0
    47  	if latency > 0 {
    48  		jitter = time2Tick(jitter)
    49  	}
    50  
    51  	reorderProb = Percentage2u32(nattrs.ReorderProb)
    52  	reorderCorr = Percentage2u32(nattrs.ReorderCorr)
    53  
    54  	if reorderProb > 0 {
    55  		// ERROR if lantency == 0
    56  		if gap == 0 {
    57  			gap = 1
    58  		}
    59  	}
    60  
    61  	corruptProb = Percentage2u32(nattrs.CorruptProb)
    62  	corruptCorr = Percentage2u32(nattrs.CorruptCorr)
    63  	rate64 = nattrs.Rate64
    64  
    65  	return &Netem{
    66  		QdiscAttrs:    attrs,
    67  		Latency:       latency,
    68  		DelayCorr:     delayCorr,
    69  		Limit:         limit,
    70  		Loss:          loss,
    71  		LossCorr:      lossCorr,
    72  		Gap:           gap,
    73  		Duplicate:     duplicate,
    74  		DuplicateCorr: duplicateCorr,
    75  		Jitter:        jitter,
    76  		ReorderProb:   reorderProb,
    77  		ReorderCorr:   reorderCorr,
    78  		CorruptProb:   corruptProb,
    79  		CorruptCorr:   corruptCorr,
    80  		Rate64:        rate64,
    81  	}
    82  }
    83  
    84  // QdiscDel will delete a qdisc from the system.
    85  // Equivalent to: `tc qdisc del $qdisc`
    86  func QdiscDel(qdisc Qdisc) error {
    87  	return pkgHandle.QdiscDel(qdisc)
    88  }
    89  
    90  // QdiscDel will delete a qdisc from the system.
    91  // Equivalent to: `tc qdisc del $qdisc`
    92  func (h *Handle) QdiscDel(qdisc Qdisc) error {
    93  	return h.qdiscModify(unix.RTM_DELQDISC, 0, qdisc)
    94  }
    95  
    96  // QdiscChange will change a qdisc in place
    97  // Equivalent to: `tc qdisc change $qdisc`
    98  // The parent and handle MUST NOT be changed.
    99  func QdiscChange(qdisc Qdisc) error {
   100  	return pkgHandle.QdiscChange(qdisc)
   101  }
   102  
   103  // QdiscChange will change a qdisc in place
   104  // Equivalent to: `tc qdisc change $qdisc`
   105  // The parent and handle MUST NOT be changed.
   106  func (h *Handle) QdiscChange(qdisc Qdisc) error {
   107  	return h.qdiscModify(unix.RTM_NEWQDISC, 0, qdisc)
   108  }
   109  
   110  // QdiscReplace will replace a qdisc to the system.
   111  // Equivalent to: `tc qdisc replace $qdisc`
   112  // The handle MUST change.
   113  func QdiscReplace(qdisc Qdisc) error {
   114  	return pkgHandle.QdiscReplace(qdisc)
   115  }
   116  
   117  // QdiscReplace will replace a qdisc to the system.
   118  // Equivalent to: `tc qdisc replace $qdisc`
   119  // The handle MUST change.
   120  func (h *Handle) QdiscReplace(qdisc Qdisc) error {
   121  	return h.qdiscModify(
   122  		unix.RTM_NEWQDISC,
   123  		unix.NLM_F_CREATE|unix.NLM_F_REPLACE,
   124  		qdisc)
   125  }
   126  
   127  // QdiscAdd will add a qdisc to the system.
   128  // Equivalent to: `tc qdisc add $qdisc`
   129  func QdiscAdd(qdisc Qdisc) error {
   130  	return pkgHandle.QdiscAdd(qdisc)
   131  }
   132  
   133  // QdiscAdd will add a qdisc to the system.
   134  // Equivalent to: `tc qdisc add $qdisc`
   135  func (h *Handle) QdiscAdd(qdisc Qdisc) error {
   136  	return h.qdiscModify(
   137  		unix.RTM_NEWQDISC,
   138  		unix.NLM_F_CREATE|unix.NLM_F_EXCL,
   139  		qdisc)
   140  }
   141  
   142  func (h *Handle) qdiscModify(cmd, flags int, qdisc Qdisc) error {
   143  	req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
   144  	base := qdisc.Attrs()
   145  	msg := &nl.TcMsg{
   146  		Family:  nl.FAMILY_ALL,
   147  		Ifindex: int32(base.LinkIndex),
   148  		Handle:  base.Handle,
   149  		Parent:  base.Parent,
   150  	}
   151  	req.AddData(msg)
   152  
   153  	// When deleting don't bother building the rest of the netlink payload
   154  	if cmd != unix.RTM_DELQDISC {
   155  		if err := qdiscPayload(req, qdisc); err != nil {
   156  			return err
   157  		}
   158  	}
   159  
   160  	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
   161  	return err
   162  }
   163  
   164  func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
   165  
   166  	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type())))
   167  	if qdisc.Attrs().IngressBlock != nil {
   168  		req.AddData(nl.NewRtAttr(nl.TCA_INGRESS_BLOCK, nl.Uint32Attr(*qdisc.Attrs().IngressBlock)))
   169  	}
   170  
   171  	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
   172  
   173  	switch qdisc := qdisc.(type) {
   174  	case *Prio:
   175  		tcmap := nl.TcPrioMap{
   176  			Bands:   int32(qdisc.Bands),
   177  			Priomap: qdisc.PriorityMap,
   178  		}
   179  		options = nl.NewRtAttr(nl.TCA_OPTIONS, tcmap.Serialize())
   180  	case *Tbf:
   181  		opt := nl.TcTbfQopt{}
   182  		opt.Rate.Rate = uint32(qdisc.Rate)
   183  		opt.Peakrate.Rate = uint32(qdisc.Peakrate)
   184  		opt.Limit = qdisc.Limit
   185  		opt.Buffer = qdisc.Buffer
   186  		options.AddRtAttr(nl.TCA_TBF_PARMS, opt.Serialize())
   187  		if qdisc.Rate >= uint64(1<<32) {
   188  			options.AddRtAttr(nl.TCA_TBF_RATE64, nl.Uint64Attr(qdisc.Rate))
   189  		}
   190  		if qdisc.Peakrate >= uint64(1<<32) {
   191  			options.AddRtAttr(nl.TCA_TBF_PRATE64, nl.Uint64Attr(qdisc.Peakrate))
   192  		}
   193  		if qdisc.Peakrate > 0 {
   194  			options.AddRtAttr(nl.TCA_TBF_PBURST, nl.Uint32Attr(qdisc.Minburst))
   195  		}
   196  	case *Htb:
   197  		opt := nl.TcHtbGlob{}
   198  		opt.Version = qdisc.Version
   199  		opt.Rate2Quantum = qdisc.Rate2Quantum
   200  		opt.Defcls = qdisc.Defcls
   201  		// TODO: Handle Debug properly. For now default to 0
   202  		opt.Debug = qdisc.Debug
   203  		opt.DirectPkts = qdisc.DirectPkts
   204  		options.AddRtAttr(nl.TCA_HTB_INIT, opt.Serialize())
   205  		if qdisc.DirectQlen != nil {
   206  			options.AddRtAttr(nl.TCA_HTB_DIRECT_QLEN, nl.Uint32Attr(*qdisc.DirectQlen))
   207  		}
   208  	case *Hfsc:
   209  		opt := nl.TcHfscOpt{}
   210  		opt.Defcls = qdisc.Defcls
   211  		options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
   212  	case *Netem:
   213  		opt := nl.TcNetemQopt{}
   214  		opt.Latency = qdisc.Latency
   215  		opt.Limit = qdisc.Limit
   216  		opt.Loss = qdisc.Loss
   217  		opt.Gap = qdisc.Gap
   218  		opt.Duplicate = qdisc.Duplicate
   219  		opt.Jitter = qdisc.Jitter
   220  		options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
   221  		// Correlation
   222  		corr := nl.TcNetemCorr{}
   223  		corr.DelayCorr = qdisc.DelayCorr
   224  		corr.LossCorr = qdisc.LossCorr
   225  		corr.DupCorr = qdisc.DuplicateCorr
   226  
   227  		if corr.DelayCorr > 0 || corr.LossCorr > 0 || corr.DupCorr > 0 {
   228  			options.AddRtAttr(nl.TCA_NETEM_CORR, corr.Serialize())
   229  		}
   230  		// Corruption
   231  		corruption := nl.TcNetemCorrupt{}
   232  		corruption.Probability = qdisc.CorruptProb
   233  		corruption.Correlation = qdisc.CorruptCorr
   234  		if corruption.Probability > 0 {
   235  			options.AddRtAttr(nl.TCA_NETEM_CORRUPT, corruption.Serialize())
   236  		}
   237  		// Reorder
   238  		reorder := nl.TcNetemReorder{}
   239  		reorder.Probability = qdisc.ReorderProb
   240  		reorder.Correlation = qdisc.ReorderCorr
   241  		if reorder.Probability > 0 {
   242  			options.AddRtAttr(nl.TCA_NETEM_REORDER, reorder.Serialize())
   243  		}
   244  		// Rate
   245  		if qdisc.Rate64 > 0 {
   246  			rate := nl.TcNetemRate{}
   247  			if qdisc.Rate64 >= uint64(1<<32) {
   248  				options.AddRtAttr(nl.TCA_NETEM_RATE64, nl.Uint64Attr(qdisc.Rate64))
   249  				rate.Rate = ^uint32(0)
   250  			} else {
   251  				rate.Rate = uint32(qdisc.Rate64)
   252  			}
   253  			options.AddRtAttr(nl.TCA_NETEM_RATE, rate.Serialize())
   254  		}
   255  	case *Clsact:
   256  		options = nil
   257  	case *Ingress:
   258  		// ingress filters must use the proper handle
   259  		if qdisc.Attrs().Parent != HANDLE_INGRESS {
   260  			return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS")
   261  		}
   262  	case *FqCodel:
   263  		options.AddRtAttr(nl.TCA_FQ_CODEL_ECN, nl.Uint32Attr((uint32(qdisc.ECN))))
   264  		if qdisc.Limit > 0 {
   265  			options.AddRtAttr(nl.TCA_FQ_CODEL_LIMIT, nl.Uint32Attr((uint32(qdisc.Limit))))
   266  		}
   267  		if qdisc.Interval > 0 {
   268  			options.AddRtAttr(nl.TCA_FQ_CODEL_INTERVAL, nl.Uint32Attr((uint32(qdisc.Interval))))
   269  		}
   270  		if qdisc.Flows > 0 {
   271  			options.AddRtAttr(nl.TCA_FQ_CODEL_FLOWS, nl.Uint32Attr((uint32(qdisc.Flows))))
   272  		}
   273  		if qdisc.Quantum > 0 {
   274  			options.AddRtAttr(nl.TCA_FQ_CODEL_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum))))
   275  		}
   276  		if qdisc.CEThreshold > 0 {
   277  			options.AddRtAttr(nl.TCA_FQ_CODEL_CE_THRESHOLD, nl.Uint32Attr(qdisc.CEThreshold))
   278  		}
   279  		if qdisc.DropBatchSize > 0 {
   280  			options.AddRtAttr(nl.TCA_FQ_CODEL_DROP_BATCH_SIZE, nl.Uint32Attr(qdisc.DropBatchSize))
   281  		}
   282  		if qdisc.MemoryLimit > 0 {
   283  			options.AddRtAttr(nl.TCA_FQ_CODEL_MEMORY_LIMIT, nl.Uint32Attr(qdisc.MemoryLimit))
   284  		}
   285  	case *Fq:
   286  		options.AddRtAttr(nl.TCA_FQ_RATE_ENABLE, nl.Uint32Attr((uint32(qdisc.Pacing))))
   287  
   288  		if qdisc.Buckets > 0 {
   289  			options.AddRtAttr(nl.TCA_FQ_BUCKETS_LOG, nl.Uint32Attr((uint32(qdisc.Buckets))))
   290  		}
   291  		if qdisc.PacketLimit > 0 {
   292  			options.AddRtAttr(nl.TCA_FQ_PLIMIT, nl.Uint32Attr((uint32(qdisc.PacketLimit))))
   293  		}
   294  		if qdisc.LowRateThreshold > 0 {
   295  			options.AddRtAttr(nl.TCA_FQ_LOW_RATE_THRESHOLD, nl.Uint32Attr((uint32(qdisc.LowRateThreshold))))
   296  		}
   297  		if qdisc.Quantum > 0 {
   298  			options.AddRtAttr(nl.TCA_FQ_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum))))
   299  		}
   300  		if qdisc.InitialQuantum > 0 {
   301  			options.AddRtAttr(nl.TCA_FQ_INITIAL_QUANTUM, nl.Uint32Attr((uint32(qdisc.InitialQuantum))))
   302  		}
   303  		if qdisc.FlowRefillDelay > 0 {
   304  			options.AddRtAttr(nl.TCA_FQ_FLOW_REFILL_DELAY, nl.Uint32Attr((uint32(qdisc.FlowRefillDelay))))
   305  		}
   306  		if qdisc.FlowPacketLimit > 0 {
   307  			options.AddRtAttr(nl.TCA_FQ_FLOW_PLIMIT, nl.Uint32Attr((uint32(qdisc.FlowPacketLimit))))
   308  		}
   309  		if qdisc.FlowMaxRate > 0 {
   310  			options.AddRtAttr(nl.TCA_FQ_FLOW_MAX_RATE, nl.Uint32Attr((uint32(qdisc.FlowMaxRate))))
   311  		}
   312  		if qdisc.FlowDefaultRate > 0 {
   313  			options.AddRtAttr(nl.TCA_FQ_FLOW_DEFAULT_RATE, nl.Uint32Attr((uint32(qdisc.FlowDefaultRate))))
   314  		}
   315  		if qdisc.Horizon > 0 {
   316  			options.AddRtAttr(nl.TCA_FQ_HORIZON, nl.Uint32Attr(qdisc.Horizon))
   317  		}
   318  		if qdisc.HorizonDropPolicy != HORIZON_DROP_POLICY_DEFAULT {
   319  			options.AddRtAttr(nl.TCA_FQ_HORIZON_DROP, nl.Uint8Attr(qdisc.HorizonDropPolicy))
   320  		}
   321  	case *Sfq:
   322  		opt := nl.TcSfqQoptV1{}
   323  		opt.TcSfqQopt.Quantum = qdisc.Quantum
   324  		opt.TcSfqQopt.Perturb = qdisc.Perturb
   325  		opt.TcSfqQopt.Limit = qdisc.Limit
   326  		opt.TcSfqQopt.Divisor = qdisc.Divisor
   327  
   328  		options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
   329  	default:
   330  		options = nil
   331  	}
   332  
   333  	if options != nil {
   334  		req.AddData(options)
   335  	}
   336  	return nil
   337  }
   338  
   339  // QdiscList gets a list of qdiscs in the system.
   340  // Equivalent to: `tc qdisc show`.
   341  // The list can be filtered by link.
   342  //
   343  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   344  // or incomplete.
   345  func QdiscList(link Link) ([]Qdisc, error) {
   346  	return pkgHandle.QdiscList(link)
   347  }
   348  
   349  // QdiscList gets a list of qdiscs in the system.
   350  // Equivalent to: `tc qdisc show`.
   351  // The list can be filtered by link.
   352  //
   353  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   354  // or incomplete.
   355  func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
   356  	req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP)
   357  	index := int32(0)
   358  	if link != nil {
   359  		base := link.Attrs()
   360  		h.ensureIndex(base)
   361  		index = int32(base.Index)
   362  	}
   363  	msg := &nl.TcMsg{
   364  		Family:  nl.FAMILY_ALL,
   365  		Ifindex: index,
   366  	}
   367  	req.AddData(msg)
   368  
   369  	msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC)
   370  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   371  		return nil, executeErr
   372  	}
   373  
   374  	var res []Qdisc
   375  	for _, m := range msgs {
   376  		msg := nl.DeserializeTcMsg(m)
   377  
   378  		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
   379  		if err != nil {
   380  			return nil, err
   381  		}
   382  
   383  		// skip qdiscs from other interfaces
   384  		if link != nil && msg.Ifindex != index {
   385  			continue
   386  		}
   387  
   388  		base := QdiscAttrs{
   389  			LinkIndex: int(msg.Ifindex),
   390  			Handle:    msg.Handle,
   391  			Parent:    msg.Parent,
   392  			Refcnt:    msg.Info,
   393  		}
   394  		var qdisc Qdisc
   395  		qdiscType := ""
   396  		for _, attr := range attrs {
   397  			switch attr.Attr.Type {
   398  			case nl.TCA_KIND:
   399  				qdiscType = string(attr.Value[:len(attr.Value)-1])
   400  				switch qdiscType {
   401  				case "pfifo_fast":
   402  					qdisc = &PfifoFast{}
   403  				case "prio":
   404  					qdisc = &Prio{}
   405  				case "tbf":
   406  					qdisc = &Tbf{}
   407  				case "ingress":
   408  					qdisc = &Ingress{}
   409  				case "htb":
   410  					qdisc = &Htb{}
   411  				case "fq":
   412  					qdisc = &Fq{}
   413  				case "hfsc":
   414  					qdisc = &Hfsc{}
   415  				case "fq_codel":
   416  					qdisc = &FqCodel{}
   417  				case "netem":
   418  					qdisc = &Netem{}
   419  				case "sfq":
   420  					qdisc = &Sfq{}
   421  				case "clsact":
   422  					qdisc = &Clsact{}
   423  				default:
   424  					qdisc = &GenericQdisc{QdiscType: qdiscType}
   425  				}
   426  			case nl.TCA_OPTIONS:
   427  				switch qdiscType {
   428  				case "pfifo_fast":
   429  					// pfifo returns TcPrioMap directly without wrapping it in rtattr
   430  					if err := parsePfifoFastData(qdisc, attr.Value); err != nil {
   431  						return nil, err
   432  					}
   433  				case "prio":
   434  					// prio returns TcPrioMap directly without wrapping it in rtattr
   435  					if err := parsePrioData(qdisc, attr.Value); err != nil {
   436  						return nil, err
   437  					}
   438  				case "tbf":
   439  					data, err := nl.ParseRouteAttr(attr.Value)
   440  					if err != nil {
   441  						return nil, err
   442  					}
   443  					if err := parseTbfData(qdisc, data); err != nil {
   444  						return nil, err
   445  					}
   446  				case "hfsc":
   447  					if err := parseHfscData(qdisc, attr.Value); err != nil {
   448  						return nil, err
   449  					}
   450  				case "htb":
   451  					data, err := nl.ParseRouteAttr(attr.Value)
   452  					if err != nil {
   453  						return nil, err
   454  					}
   455  					if err := parseHtbData(qdisc, data); err != nil {
   456  						return nil, err
   457  					}
   458  				case "fq":
   459  					data, err := nl.ParseRouteAttr(attr.Value)
   460  					if err != nil {
   461  						return nil, err
   462  					}
   463  					if err := parseFqData(qdisc, data); err != nil {
   464  						return nil, err
   465  					}
   466  				case "fq_codel":
   467  					data, err := nl.ParseRouteAttr(attr.Value)
   468  					if err != nil {
   469  						return nil, err
   470  					}
   471  					if err := parseFqCodelData(qdisc, data); err != nil {
   472  						return nil, err
   473  					}
   474  				case "netem":
   475  					if err := parseNetemData(qdisc, attr.Value); err != nil {
   476  						return nil, err
   477  					}
   478  				case "sfq":
   479  					if err := parseSfqData(qdisc, attr.Value); err != nil {
   480  						return nil, err
   481  					}
   482  
   483  					// no options for ingress
   484  				}
   485  			case nl.TCA_INGRESS_BLOCK:
   486  				ingressBlock := new(uint32)
   487  				*ingressBlock = native.Uint32(attr.Value)
   488  				base.IngressBlock = ingressBlock
   489  			case nl.TCA_STATS:
   490  				s, err := parseTcStats(attr.Value)
   491  				if err != nil {
   492  					return nil, err
   493  				}
   494  				base.Statistics = (*QdiscStatistics)(s)
   495  			case nl.TCA_STATS2:
   496  				s, err := parseTcStats2(attr.Value)
   497  				if err != nil {
   498  					return nil, err
   499  				}
   500  				base.Statistics = (*QdiscStatistics)(s)
   501  			}
   502  		}
   503  		*qdisc.Attrs() = base
   504  		res = append(res, qdisc)
   505  	}
   506  
   507  	return res, executeErr
   508  }
   509  
   510  func parsePfifoFastData(qdisc Qdisc, value []byte) error {
   511  	pfifo := qdisc.(*PfifoFast)
   512  	tcmap := nl.DeserializeTcPrioMap(value)
   513  	pfifo.PriorityMap = tcmap.Priomap
   514  	pfifo.Bands = uint8(tcmap.Bands)
   515  	return nil
   516  }
   517  
   518  func parsePrioData(qdisc Qdisc, value []byte) error {
   519  	prio := qdisc.(*Prio)
   520  	tcmap := nl.DeserializeTcPrioMap(value)
   521  	prio.PriorityMap = tcmap.Priomap
   522  	prio.Bands = uint8(tcmap.Bands)
   523  	return nil
   524  }
   525  
   526  func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
   527  	htb := qdisc.(*Htb)
   528  	for _, datum := range data {
   529  		switch datum.Attr.Type {
   530  		case nl.TCA_HTB_INIT:
   531  			opt := nl.DeserializeTcHtbGlob(datum.Value)
   532  			htb.Version = opt.Version
   533  			htb.Rate2Quantum = opt.Rate2Quantum
   534  			htb.Defcls = opt.Defcls
   535  			htb.Debug = opt.Debug
   536  			htb.DirectPkts = opt.DirectPkts
   537  		case nl.TCA_HTB_DIRECT_QLEN:
   538  			directQlen := native.Uint32(datum.Value)
   539  			htb.DirectQlen = &directQlen
   540  		}
   541  	}
   542  	return nil
   543  }
   544  
   545  func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
   546  	fqCodel := qdisc.(*FqCodel)
   547  	for _, datum := range data {
   548  
   549  		switch datum.Attr.Type {
   550  		case nl.TCA_FQ_CODEL_TARGET:
   551  			fqCodel.Target = native.Uint32(datum.Value)
   552  		case nl.TCA_FQ_CODEL_LIMIT:
   553  			fqCodel.Limit = native.Uint32(datum.Value)
   554  		case nl.TCA_FQ_CODEL_INTERVAL:
   555  			fqCodel.Interval = native.Uint32(datum.Value)
   556  		case nl.TCA_FQ_CODEL_ECN:
   557  			fqCodel.ECN = native.Uint32(datum.Value)
   558  		case nl.TCA_FQ_CODEL_FLOWS:
   559  			fqCodel.Flows = native.Uint32(datum.Value)
   560  		case nl.TCA_FQ_CODEL_QUANTUM:
   561  			fqCodel.Quantum = native.Uint32(datum.Value)
   562  		case nl.TCA_FQ_CODEL_CE_THRESHOLD:
   563  			fqCodel.CEThreshold = native.Uint32(datum.Value)
   564  		case nl.TCA_FQ_CODEL_DROP_BATCH_SIZE:
   565  			fqCodel.DropBatchSize = native.Uint32(datum.Value)
   566  		case nl.TCA_FQ_CODEL_MEMORY_LIMIT:
   567  			fqCodel.MemoryLimit = native.Uint32(datum.Value)
   568  		}
   569  	}
   570  	return nil
   571  }
   572  
   573  func parseHfscData(qdisc Qdisc, data []byte) error {
   574  	Hfsc := qdisc.(*Hfsc)
   575  	Hfsc.Defcls = native.Uint16(data)
   576  	return nil
   577  }
   578  
   579  func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
   580  	fq := qdisc.(*Fq)
   581  	for _, datum := range data {
   582  		switch datum.Attr.Type {
   583  		case nl.TCA_FQ_BUCKETS_LOG:
   584  			fq.Buckets = native.Uint32(datum.Value)
   585  		case nl.TCA_FQ_LOW_RATE_THRESHOLD:
   586  			fq.LowRateThreshold = native.Uint32(datum.Value)
   587  		case nl.TCA_FQ_QUANTUM:
   588  			fq.Quantum = native.Uint32(datum.Value)
   589  		case nl.TCA_FQ_RATE_ENABLE:
   590  			fq.Pacing = native.Uint32(datum.Value)
   591  		case nl.TCA_FQ_INITIAL_QUANTUM:
   592  			fq.InitialQuantum = native.Uint32(datum.Value)
   593  		case nl.TCA_FQ_ORPHAN_MASK:
   594  			// TODO
   595  		case nl.TCA_FQ_FLOW_REFILL_DELAY:
   596  			fq.FlowRefillDelay = native.Uint32(datum.Value)
   597  		case nl.TCA_FQ_FLOW_PLIMIT:
   598  			fq.FlowPacketLimit = native.Uint32(datum.Value)
   599  		case nl.TCA_FQ_PLIMIT:
   600  			fq.PacketLimit = native.Uint32(datum.Value)
   601  		case nl.TCA_FQ_FLOW_MAX_RATE:
   602  			fq.FlowMaxRate = native.Uint32(datum.Value)
   603  		case nl.TCA_FQ_FLOW_DEFAULT_RATE:
   604  			fq.FlowDefaultRate = native.Uint32(datum.Value)
   605  		case nl.TCA_FQ_HORIZON:
   606  			fq.Horizon = native.Uint32(datum.Value)
   607  		case nl.TCA_FQ_HORIZON_DROP:
   608  			fq.HorizonDropPolicy = datum.Value[0]
   609  
   610  		}
   611  	}
   612  	return nil
   613  }
   614  
   615  func parseNetemData(qdisc Qdisc, value []byte) error {
   616  	netem := qdisc.(*Netem)
   617  	opt := nl.DeserializeTcNetemQopt(value)
   618  	netem.Latency = opt.Latency
   619  	netem.Limit = opt.Limit
   620  	netem.Loss = opt.Loss
   621  	netem.Gap = opt.Gap
   622  	netem.Duplicate = opt.Duplicate
   623  	netem.Jitter = opt.Jitter
   624  	data, err := nl.ParseRouteAttr(value[nl.SizeofTcNetemQopt:])
   625  	if err != nil {
   626  		return err
   627  	}
   628  	var rate *nl.TcNetemRate
   629  	var rate64 uint64
   630  	for _, datum := range data {
   631  		switch datum.Attr.Type {
   632  		case nl.TCA_NETEM_CORR:
   633  			opt := nl.DeserializeTcNetemCorr(datum.Value)
   634  			netem.DelayCorr = opt.DelayCorr
   635  			netem.LossCorr = opt.LossCorr
   636  			netem.DuplicateCorr = opt.DupCorr
   637  		case nl.TCA_NETEM_CORRUPT:
   638  			opt := nl.DeserializeTcNetemCorrupt(datum.Value)
   639  			netem.CorruptProb = opt.Probability
   640  			netem.CorruptCorr = opt.Correlation
   641  		case nl.TCA_NETEM_REORDER:
   642  			opt := nl.DeserializeTcNetemReorder(datum.Value)
   643  			netem.ReorderProb = opt.Probability
   644  			netem.ReorderCorr = opt.Correlation
   645  		case nl.TCA_NETEM_RATE:
   646  			rate = nl.DeserializeTcNetemRate(datum.Value)
   647  		case nl.TCA_NETEM_RATE64:
   648  			rate64 = native.Uint64(datum.Value)
   649  		}
   650  	}
   651  	if rate != nil {
   652  		netem.Rate64 = uint64(rate.Rate)
   653  		if rate64 > 0 {
   654  			netem.Rate64 = rate64
   655  		}
   656  	}
   657  
   658  	return nil
   659  }
   660  
   661  func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
   662  	tbf := qdisc.(*Tbf)
   663  	for _, datum := range data {
   664  		switch datum.Attr.Type {
   665  		case nl.TCA_TBF_PARMS:
   666  			opt := nl.DeserializeTcTbfQopt(datum.Value)
   667  			tbf.Rate = uint64(opt.Rate.Rate)
   668  			tbf.Peakrate = uint64(opt.Peakrate.Rate)
   669  			tbf.Limit = opt.Limit
   670  			tbf.Buffer = opt.Buffer
   671  		case nl.TCA_TBF_RATE64:
   672  			tbf.Rate = native.Uint64(datum.Value[0:8])
   673  		case nl.TCA_TBF_PRATE64:
   674  			tbf.Peakrate = native.Uint64(datum.Value[0:8])
   675  		case nl.TCA_TBF_PBURST:
   676  			tbf.Minburst = native.Uint32(datum.Value[0:4])
   677  		}
   678  	}
   679  	return nil
   680  }
   681  
   682  func parseSfqData(qdisc Qdisc, value []byte) error {
   683  	sfq := qdisc.(*Sfq)
   684  	opt := nl.DeserializeTcSfqQoptV1(value)
   685  	sfq.Quantum = opt.TcSfqQopt.Quantum
   686  	sfq.Perturb = opt.TcSfqQopt.Perturb
   687  	sfq.Limit = opt.TcSfqQopt.Limit
   688  	sfq.Divisor = opt.TcSfqQopt.Divisor
   689  
   690  	return nil
   691  }
   692  
   693  const (
   694  	TIME_UNITS_PER_SEC = 1000000
   695  )
   696  
   697  var (
   698  	tickInUsec  float64
   699  	clockFactor float64
   700  	hz          float64
   701  
   702  	// Without this, the go race detector may report races.
   703  	initClockMutex sync.Mutex
   704  )
   705  
   706  func initClock() {
   707  	data, err := ioutil.ReadFile("/proc/net/psched")
   708  	if err != nil {
   709  		return
   710  	}
   711  	parts := strings.Split(strings.TrimSpace(string(data)), " ")
   712  	if len(parts) < 4 {
   713  		return
   714  	}
   715  	var vals [4]uint64
   716  	for i := range vals {
   717  		val, err := strconv.ParseUint(parts[i], 16, 32)
   718  		if err != nil {
   719  			return
   720  		}
   721  		vals[i] = val
   722  	}
   723  	// compatibility
   724  	if vals[2] == 1000000000 {
   725  		vals[0] = vals[1]
   726  	}
   727  	clockFactor = float64(vals[2]) / TIME_UNITS_PER_SEC
   728  	tickInUsec = float64(vals[0]) / float64(vals[1]) * clockFactor
   729  	if vals[2] == 1000000 {
   730  		// ref https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/lib/utils.c#n963
   731  		hz = float64(vals[3])
   732  	} else {
   733  		hz = 100
   734  	}
   735  }
   736  
   737  func TickInUsec() float64 {
   738  	initClockMutex.Lock()
   739  	defer initClockMutex.Unlock()
   740  	if tickInUsec == 0.0 {
   741  		initClock()
   742  	}
   743  	return tickInUsec
   744  }
   745  
   746  func ClockFactor() float64 {
   747  	initClockMutex.Lock()
   748  	defer initClockMutex.Unlock()
   749  	if clockFactor == 0.0 {
   750  		initClock()
   751  	}
   752  	return clockFactor
   753  }
   754  
   755  func Hz() float64 {
   756  	initClockMutex.Lock()
   757  	defer initClockMutex.Unlock()
   758  	if hz == 0.0 {
   759  		initClock()
   760  	}
   761  	return hz
   762  }
   763  
   764  func time2Tick(time uint32) uint32 {
   765  	return uint32(float64(time) * TickInUsec())
   766  }
   767  
   768  func tick2Time(tick uint32) uint32 {
   769  	return uint32(float64(tick) / TickInUsec())
   770  }
   771  
   772  func time2Ktime(time uint32) uint32 {
   773  	return uint32(float64(time) * ClockFactor())
   774  }
   775  
   776  func ktime2Time(ktime uint32) uint32 {
   777  	return uint32(float64(ktime) / ClockFactor())
   778  }
   779  
   780  func burst(rate uint64, buffer uint32) uint32 {
   781  	return uint32(float64(rate) * float64(tick2Time(buffer)) / TIME_UNITS_PER_SEC)
   782  }
   783  
   784  func latency(rate uint64, limit, buffer uint32) float64 {
   785  	return TIME_UNITS_PER_SEC*(float64(limit)/float64(rate)) - float64(tick2Time(buffer))
   786  }
   787  
   788  func Xmittime(rate uint64, size uint32) uint32 {
   789  	// https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/tc/tc_core.c#n62
   790  	return time2Tick(uint32(TIME_UNITS_PER_SEC * (float64(size) / float64(rate))))
   791  }
   792  
   793  func Xmitsize(rate uint64, ticks uint32) uint32 {
   794  	return uint32((float64(rate) * float64(tick2Time(ticks))) / TIME_UNITS_PER_SEC)
   795  }