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

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