github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/qdisc_linux.go (about)

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