1package netlink
2
3import (
4	"fmt"
5	"io/ioutil"
6	"strconv"
7	"strings"
8	"syscall"
9
10	"github.com/vishvananda/netlink/nl"
11	"golang.org/x/sys/unix"
12)
13
14// NOTE function is here because it uses other linux functions
15func 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`
81func 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`
87func (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.
94func 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.
101func (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.
108func 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.
115func (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`
124func 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`
130func (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
137func (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
159func 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
254	case *Fq:
255		options.AddRtAttr(nl.TCA_FQ_RATE_ENABLE, nl.Uint32Attr((uint32(qdisc.Pacing))))
256
257		if qdisc.Buckets > 0 {
258			options.AddRtAttr(nl.TCA_FQ_BUCKETS_LOG, nl.Uint32Attr((uint32(qdisc.Buckets))))
259		}
260		if qdisc.LowRateThreshold > 0 {
261			options.AddRtAttr(nl.TCA_FQ_LOW_RATE_THRESHOLD, nl.Uint32Attr((uint32(qdisc.LowRateThreshold))))
262		}
263		if qdisc.Quantum > 0 {
264			options.AddRtAttr(nl.TCA_FQ_QUANTUM, nl.Uint32Attr((uint32(qdisc.Quantum))))
265		}
266		if qdisc.InitialQuantum > 0 {
267			options.AddRtAttr(nl.TCA_FQ_INITIAL_QUANTUM, nl.Uint32Attr((uint32(qdisc.InitialQuantum))))
268		}
269		if qdisc.FlowRefillDelay > 0 {
270			options.AddRtAttr(nl.TCA_FQ_FLOW_REFILL_DELAY, nl.Uint32Attr((uint32(qdisc.FlowRefillDelay))))
271		}
272		if qdisc.FlowPacketLimit > 0 {
273			options.AddRtAttr(nl.TCA_FQ_FLOW_PLIMIT, nl.Uint32Attr((uint32(qdisc.FlowPacketLimit))))
274		}
275		if qdisc.FlowMaxRate > 0 {
276			options.AddRtAttr(nl.TCA_FQ_FLOW_MAX_RATE, nl.Uint32Attr((uint32(qdisc.FlowMaxRate))))
277		}
278		if qdisc.FlowDefaultRate > 0 {
279			options.AddRtAttr(nl.TCA_FQ_FLOW_DEFAULT_RATE, nl.Uint32Attr((uint32(qdisc.FlowDefaultRate))))
280		}
281	default:
282		options = nil
283	}
284
285	if options != nil {
286		req.AddData(options)
287	}
288	return nil
289}
290
291// QdiscList gets a list of qdiscs in the system.
292// Equivalent to: `tc qdisc show`.
293// The list can be filtered by link.
294func QdiscList(link Link) ([]Qdisc, error) {
295	return pkgHandle.QdiscList(link)
296}
297
298// QdiscList gets a list of qdiscs in the system.
299// Equivalent to: `tc qdisc show`.
300// The list can be filtered by link.
301func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
302	req := h.newNetlinkRequest(unix.RTM_GETQDISC, unix.NLM_F_DUMP)
303	index := int32(0)
304	if link != nil {
305		base := link.Attrs()
306		h.ensureIndex(base)
307		index = int32(base.Index)
308	}
309	msg := &nl.TcMsg{
310		Family:  nl.FAMILY_ALL,
311		Ifindex: index,
312	}
313	req.AddData(msg)
314
315	msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWQDISC)
316	if err != nil {
317		return nil, err
318	}
319
320	var res []Qdisc
321	for _, m := range msgs {
322		msg := nl.DeserializeTcMsg(m)
323
324		attrs, err := nl.ParseRouteAttr(m[msg.Len():])
325		if err != nil {
326			return nil, err
327		}
328
329		// skip qdiscs from other interfaces
330		if link != nil && msg.Ifindex != index {
331			continue
332		}
333
334		base := QdiscAttrs{
335			LinkIndex: int(msg.Ifindex),
336			Handle:    msg.Handle,
337			Parent:    msg.Parent,
338			Refcnt:    msg.Info,
339		}
340		var qdisc Qdisc
341		qdiscType := ""
342		for _, attr := range attrs {
343			switch attr.Attr.Type {
344			case nl.TCA_KIND:
345				qdiscType = string(attr.Value[:len(attr.Value)-1])
346				switch qdiscType {
347				case "pfifo_fast":
348					qdisc = &PfifoFast{}
349				case "prio":
350					qdisc = &Prio{}
351				case "tbf":
352					qdisc = &Tbf{}
353				case "ingress":
354					qdisc = &Ingress{}
355				case "htb":
356					qdisc = &Htb{}
357				case "fq":
358					qdisc = &Fq{}
359				case "hfsc":
360					qdisc = &Hfsc{}
361				case "fq_codel":
362					qdisc = &FqCodel{}
363				case "netem":
364					qdisc = &Netem{}
365				default:
366					qdisc = &GenericQdisc{QdiscType: qdiscType}
367				}
368			case nl.TCA_OPTIONS:
369				switch qdiscType {
370				case "pfifo_fast":
371					// pfifo returns TcPrioMap directly without wrapping it in rtattr
372					if err := parsePfifoFastData(qdisc, attr.Value); err != nil {
373						return nil, err
374					}
375				case "prio":
376					// prio returns TcPrioMap directly without wrapping it in rtattr
377					if err := parsePrioData(qdisc, attr.Value); err != nil {
378						return nil, err
379					}
380				case "tbf":
381					data, err := nl.ParseRouteAttr(attr.Value)
382					if err != nil {
383						return nil, err
384					}
385					if err := parseTbfData(qdisc, data); err != nil {
386						return nil, err
387					}
388				case "hfsc":
389					if err := parseHfscData(qdisc, attr.Value); err != nil {
390						return nil, err
391					}
392				case "htb":
393					data, err := nl.ParseRouteAttr(attr.Value)
394					if err != nil {
395						return nil, err
396					}
397					if err := parseHtbData(qdisc, data); err != nil {
398						return nil, err
399					}
400				case "fq":
401					data, err := nl.ParseRouteAttr(attr.Value)
402					if err != nil {
403						return nil, err
404					}
405					if err := parseFqData(qdisc, data); err != nil {
406						return nil, err
407					}
408				case "fq_codel":
409					data, err := nl.ParseRouteAttr(attr.Value)
410					if err != nil {
411						return nil, err
412					}
413					if err := parseFqCodelData(qdisc, data); err != nil {
414						return nil, err
415					}
416				case "netem":
417					if err := parseNetemData(qdisc, attr.Value); err != nil {
418						return nil, err
419					}
420
421					// no options for ingress
422				}
423			}
424		}
425		*qdisc.Attrs() = base
426		res = append(res, qdisc)
427	}
428
429	return res, nil
430}
431
432func parsePfifoFastData(qdisc Qdisc, value []byte) error {
433	pfifo := qdisc.(*PfifoFast)
434	tcmap := nl.DeserializeTcPrioMap(value)
435	pfifo.PriorityMap = tcmap.Priomap
436	pfifo.Bands = uint8(tcmap.Bands)
437	return nil
438}
439
440func parsePrioData(qdisc Qdisc, value []byte) error {
441	prio := qdisc.(*Prio)
442	tcmap := nl.DeserializeTcPrioMap(value)
443	prio.PriorityMap = tcmap.Priomap
444	prio.Bands = uint8(tcmap.Bands)
445	return nil
446}
447
448func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
449	native = nl.NativeEndian()
450	htb := qdisc.(*Htb)
451	for _, datum := range data {
452		switch datum.Attr.Type {
453		case nl.TCA_HTB_INIT:
454			opt := nl.DeserializeTcHtbGlob(datum.Value)
455			htb.Version = opt.Version
456			htb.Rate2Quantum = opt.Rate2Quantum
457			htb.Defcls = opt.Defcls
458			htb.Debug = opt.Debug
459			htb.DirectPkts = opt.DirectPkts
460		case nl.TCA_HTB_DIRECT_QLEN:
461			// TODO
462			//htb.DirectQlen = native.uint32(datum.Value)
463		}
464	}
465	return nil
466}
467
468func parseFqCodelData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
469	native = nl.NativeEndian()
470	fqCodel := qdisc.(*FqCodel)
471	for _, datum := range data {
472
473		switch datum.Attr.Type {
474		case nl.TCA_FQ_CODEL_TARGET:
475			fqCodel.Target = native.Uint32(datum.Value)
476		case nl.TCA_FQ_CODEL_LIMIT:
477			fqCodel.Limit = native.Uint32(datum.Value)
478		case nl.TCA_FQ_CODEL_INTERVAL:
479			fqCodel.Interval = native.Uint32(datum.Value)
480		case nl.TCA_FQ_CODEL_ECN:
481			fqCodel.ECN = native.Uint32(datum.Value)
482		case nl.TCA_FQ_CODEL_FLOWS:
483			fqCodel.Flows = native.Uint32(datum.Value)
484		case nl.TCA_FQ_CODEL_QUANTUM:
485			fqCodel.Quantum = native.Uint32(datum.Value)
486		}
487	}
488	return nil
489}
490
491func parseHfscData(qdisc Qdisc, data []byte) error {
492	Hfsc := qdisc.(*Hfsc)
493	native = nl.NativeEndian()
494	Hfsc.Defcls = native.Uint16(data)
495	return nil
496}
497
498func parseFqData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
499	native = nl.NativeEndian()
500	fq := qdisc.(*Fq)
501	for _, datum := range data {
502		switch datum.Attr.Type {
503		case nl.TCA_FQ_BUCKETS_LOG:
504			fq.Buckets = native.Uint32(datum.Value)
505		case nl.TCA_FQ_LOW_RATE_THRESHOLD:
506			fq.LowRateThreshold = native.Uint32(datum.Value)
507		case nl.TCA_FQ_QUANTUM:
508			fq.Quantum = native.Uint32(datum.Value)
509		case nl.TCA_FQ_RATE_ENABLE:
510			fq.Pacing = native.Uint32(datum.Value)
511		case nl.TCA_FQ_INITIAL_QUANTUM:
512			fq.InitialQuantum = native.Uint32(datum.Value)
513		case nl.TCA_FQ_ORPHAN_MASK:
514			// TODO
515		case nl.TCA_FQ_FLOW_REFILL_DELAY:
516			fq.FlowRefillDelay = native.Uint32(datum.Value)
517		case nl.TCA_FQ_FLOW_PLIMIT:
518			fq.FlowPacketLimit = native.Uint32(datum.Value)
519		case nl.TCA_FQ_PLIMIT:
520			fq.PacketLimit = native.Uint32(datum.Value)
521		case nl.TCA_FQ_FLOW_MAX_RATE:
522			fq.FlowMaxRate = native.Uint32(datum.Value)
523		case nl.TCA_FQ_FLOW_DEFAULT_RATE:
524			fq.FlowDefaultRate = native.Uint32(datum.Value)
525		}
526	}
527	return nil
528}
529
530func parseNetemData(qdisc Qdisc, value []byte) error {
531	netem := qdisc.(*Netem)
532	opt := nl.DeserializeTcNetemQopt(value)
533	netem.Latency = opt.Latency
534	netem.Limit = opt.Limit
535	netem.Loss = opt.Loss
536	netem.Gap = opt.Gap
537	netem.Duplicate = opt.Duplicate
538	netem.Jitter = opt.Jitter
539	data, err := nl.ParseRouteAttr(value[nl.SizeofTcNetemQopt:])
540	if err != nil {
541		return err
542	}
543	for _, datum := range data {
544		switch datum.Attr.Type {
545		case nl.TCA_NETEM_CORR:
546			opt := nl.DeserializeTcNetemCorr(datum.Value)
547			netem.DelayCorr = opt.DelayCorr
548			netem.LossCorr = opt.LossCorr
549			netem.DuplicateCorr = opt.DupCorr
550		case nl.TCA_NETEM_CORRUPT:
551			opt := nl.DeserializeTcNetemCorrupt(datum.Value)
552			netem.CorruptProb = opt.Probability
553			netem.CorruptCorr = opt.Correlation
554		case nl.TCA_NETEM_REORDER:
555			opt := nl.DeserializeTcNetemReorder(datum.Value)
556			netem.ReorderProb = opt.Probability
557			netem.ReorderCorr = opt.Correlation
558		}
559	}
560	return nil
561}
562
563func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
564	native = nl.NativeEndian()
565	tbf := qdisc.(*Tbf)
566	for _, datum := range data {
567		switch datum.Attr.Type {
568		case nl.TCA_TBF_PARMS:
569			opt := nl.DeserializeTcTbfQopt(datum.Value)
570			tbf.Rate = uint64(opt.Rate.Rate)
571			tbf.Peakrate = uint64(opt.Peakrate.Rate)
572			tbf.Limit = opt.Limit
573			tbf.Buffer = opt.Buffer
574		case nl.TCA_TBF_RATE64:
575			tbf.Rate = native.Uint64(datum.Value[0:8])
576		case nl.TCA_TBF_PRATE64:
577			tbf.Peakrate = native.Uint64(datum.Value[0:8])
578		case nl.TCA_TBF_PBURST:
579			tbf.Minburst = native.Uint32(datum.Value[0:4])
580		}
581	}
582	return nil
583}
584
585const (
586	TIME_UNITS_PER_SEC = 1000000
587)
588
589var (
590	tickInUsec  float64
591	clockFactor float64
592	hz          float64
593)
594
595func initClock() {
596	data, err := ioutil.ReadFile("/proc/net/psched")
597	if err != nil {
598		return
599	}
600	parts := strings.Split(strings.TrimSpace(string(data)), " ")
601	if len(parts) < 3 {
602		return
603	}
604	var vals [3]uint64
605	for i := range vals {
606		val, err := strconv.ParseUint(parts[i], 16, 32)
607		if err != nil {
608			return
609		}
610		vals[i] = val
611	}
612	// compatibility
613	if vals[2] == 1000000000 {
614		vals[0] = vals[1]
615	}
616	clockFactor = float64(vals[2]) / TIME_UNITS_PER_SEC
617	tickInUsec = float64(vals[0]) / float64(vals[1]) * clockFactor
618	hz = float64(vals[0])
619}
620
621func TickInUsec() float64 {
622	if tickInUsec == 0.0 {
623		initClock()
624	}
625	return tickInUsec
626}
627
628func ClockFactor() float64 {
629	if clockFactor == 0.0 {
630		initClock()
631	}
632	return clockFactor
633}
634
635func Hz() float64 {
636	if hz == 0.0 {
637		initClock()
638	}
639	return hz
640}
641
642func time2Tick(time uint32) uint32 {
643	return uint32(float64(time) * TickInUsec())
644}
645
646func tick2Time(tick uint32) uint32 {
647	return uint32(float64(tick) / TickInUsec())
648}
649
650func time2Ktime(time uint32) uint32 {
651	return uint32(float64(time) * ClockFactor())
652}
653
654func ktime2Time(ktime uint32) uint32 {
655	return uint32(float64(ktime) / ClockFactor())
656}
657
658func burst(rate uint64, buffer uint32) uint32 {
659	return uint32(float64(rate) * float64(tick2Time(buffer)) / TIME_UNITS_PER_SEC)
660}
661
662func latency(rate uint64, limit, buffer uint32) float64 {
663	return TIME_UNITS_PER_SEC*(float64(limit)/float64(rate)) - float64(tick2Time(buffer))
664}
665
666func Xmittime(rate uint64, size uint32) float64 {
667	return TickInUsec() * TIME_UNITS_PER_SEC * (float64(size) / float64(rate))
668}
669