1package netlink
2
3import (
4	"fmt"
5	"math"
6)
7
8const (
9	HANDLE_NONE      = 0
10	HANDLE_INGRESS   = 0xFFFFFFF1
11	HANDLE_CLSACT    = HANDLE_INGRESS
12	HANDLE_ROOT      = 0xFFFFFFFF
13	PRIORITY_MAP_LEN = 16
14)
15const (
16	HANDLE_MIN_INGRESS = 0xFFFFFFF2
17	HANDLE_MIN_EGRESS  = 0xFFFFFFF3
18)
19
20type Qdisc interface {
21	Attrs() *QdiscAttrs
22	Type() string
23}
24
25// QdiscAttrs represents a netlink qdisc. A qdisc is associated with a link,
26// has a handle, a parent and a refcnt. The root qdisc of a device should
27// have parent == HANDLE_ROOT.
28type QdiscAttrs struct {
29	LinkIndex int
30	Handle    uint32
31	Parent    uint32
32	Refcnt    uint32 // read only
33}
34
35func (q QdiscAttrs) String() string {
36	return fmt.Sprintf("{LinkIndex: %d, Handle: %s, Parent: %s, Refcnt: %d}", q.LinkIndex, HandleStr(q.Handle), HandleStr(q.Parent), q.Refcnt)
37}
38
39func MakeHandle(major, minor uint16) uint32 {
40	return (uint32(major) << 16) | uint32(minor)
41}
42
43func MajorMinor(handle uint32) (uint16, uint16) {
44	return uint16((handle & 0xFFFF0000) >> 16), uint16(handle & 0x0000FFFFF)
45}
46
47func HandleStr(handle uint32) string {
48	switch handle {
49	case HANDLE_NONE:
50		return "none"
51	case HANDLE_INGRESS:
52		return "ingress"
53	case HANDLE_ROOT:
54		return "root"
55	default:
56		major, minor := MajorMinor(handle)
57		return fmt.Sprintf("%x:%x", major, minor)
58	}
59}
60
61func Percentage2u32(percentage float32) uint32 {
62	// FIXME this is most likely not the best way to convert from % to uint32
63	if percentage == 100 {
64		return math.MaxUint32
65	}
66	return uint32(math.MaxUint32 * (percentage / 100))
67}
68
69// PfifoFast is the default qdisc created by the kernel if one has not
70// been defined for the interface
71type PfifoFast struct {
72	QdiscAttrs
73	Bands       uint8
74	PriorityMap [PRIORITY_MAP_LEN]uint8
75}
76
77func (qdisc *PfifoFast) Attrs() *QdiscAttrs {
78	return &qdisc.QdiscAttrs
79}
80
81func (qdisc *PfifoFast) Type() string {
82	return "pfifo_fast"
83}
84
85// Prio is a basic qdisc that works just like PfifoFast
86type Prio struct {
87	QdiscAttrs
88	Bands       uint8
89	PriorityMap [PRIORITY_MAP_LEN]uint8
90}
91
92func NewPrio(attrs QdiscAttrs) *Prio {
93	return &Prio{
94		QdiscAttrs:  attrs,
95		Bands:       3,
96		PriorityMap: [PRIORITY_MAP_LEN]uint8{1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
97	}
98}
99
100func (qdisc *Prio) Attrs() *QdiscAttrs {
101	return &qdisc.QdiscAttrs
102}
103
104func (qdisc *Prio) Type() string {
105	return "prio"
106}
107
108// Htb is a classful qdisc that rate limits based on tokens
109type Htb struct {
110	QdiscAttrs
111	Version      uint32
112	Rate2Quantum uint32
113	Defcls       uint32
114	Debug        uint32
115	DirectPkts   uint32
116}
117
118func NewHtb(attrs QdiscAttrs) *Htb {
119	return &Htb{
120		QdiscAttrs:   attrs,
121		Version:      3,
122		Defcls:       0,
123		Rate2Quantum: 10,
124		Debug:        0,
125		DirectPkts:   0,
126	}
127}
128
129func (qdisc *Htb) Attrs() *QdiscAttrs {
130	return &qdisc.QdiscAttrs
131}
132
133func (qdisc *Htb) Type() string {
134	return "htb"
135}
136
137// Netem is a classless qdisc that rate limits based on tokens
138
139type NetemQdiscAttrs struct {
140	Latency       uint32  // in us
141	DelayCorr     float32 // in %
142	Limit         uint32
143	Loss          float32 // in %
144	LossCorr      float32 // in %
145	Gap           uint32
146	Duplicate     float32 // in %
147	DuplicateCorr float32 // in %
148	Jitter        uint32  // in us
149	ReorderProb   float32 // in %
150	ReorderCorr   float32 // in %
151	CorruptProb   float32 // in %
152	CorruptCorr   float32 // in %
153}
154
155func (q NetemQdiscAttrs) String() string {
156	return fmt.Sprintf(
157		"{Latency: %d, Limit: %d, Loss: %f, Gap: %d, Duplicate: %f, Jitter: %d}",
158		q.Latency, q.Limit, q.Loss, q.Gap, q.Duplicate, q.Jitter,
159	)
160}
161
162type Netem struct {
163	QdiscAttrs
164	Latency       uint32
165	DelayCorr     uint32
166	Limit         uint32
167	Loss          uint32
168	LossCorr      uint32
169	Gap           uint32
170	Duplicate     uint32
171	DuplicateCorr uint32
172	Jitter        uint32
173	ReorderProb   uint32
174	ReorderCorr   uint32
175	CorruptProb   uint32
176	CorruptCorr   uint32
177}
178
179func (netem *Netem) String() string {
180	return fmt.Sprintf(
181		"{Latency: %v, Limit: %v, Loss: %v, Gap: %v, Duplicate: %v, Jitter: %v}",
182		netem.Latency, netem.Limit, netem.Loss, netem.Gap, netem.Duplicate, netem.Jitter,
183	)
184}
185
186func (qdisc *Netem) Attrs() *QdiscAttrs {
187	return &qdisc.QdiscAttrs
188}
189
190func (qdisc *Netem) Type() string {
191	return "netem"
192}
193
194// Tbf is a classless qdisc that rate limits based on tokens
195type Tbf struct {
196	QdiscAttrs
197	Rate     uint64
198	Limit    uint32
199	Buffer   uint32
200	Peakrate uint64
201	Minburst uint32
202	// TODO: handle other settings
203}
204
205func (qdisc *Tbf) Attrs() *QdiscAttrs {
206	return &qdisc.QdiscAttrs
207}
208
209func (qdisc *Tbf) Type() string {
210	return "tbf"
211}
212
213// Ingress is a qdisc for adding ingress filters
214type Ingress struct {
215	QdiscAttrs
216}
217
218func (qdisc *Ingress) Attrs() *QdiscAttrs {
219	return &qdisc.QdiscAttrs
220}
221
222func (qdisc *Ingress) Type() string {
223	return "ingress"
224}
225
226// GenericQdisc qdiscs represent types that are not currently understood
227// by this netlink library.
228type GenericQdisc struct {
229	QdiscAttrs
230	QdiscType string
231}
232
233func (qdisc *GenericQdisc) Attrs() *QdiscAttrs {
234	return &qdisc.QdiscAttrs
235}
236
237func (qdisc *GenericQdisc) Type() string {
238	return qdisc.QdiscType
239}
240
241type Hfsc struct {
242	QdiscAttrs
243	Defcls uint16
244}
245
246func NewHfsc(attrs QdiscAttrs) *Hfsc {
247	return &Hfsc{
248		QdiscAttrs: attrs,
249		Defcls:     1,
250	}
251}
252
253func (hfsc *Hfsc) Attrs() *QdiscAttrs {
254	return &hfsc.QdiscAttrs
255}
256
257func (hfsc *Hfsc) Type() string {
258	return "hfsc"
259}
260
261func (hfsc *Hfsc) String() string {
262	return fmt.Sprintf(
263		"{%v -- default: %d}",
264		hfsc.Attrs(), hfsc.Defcls,
265	)
266}
267
268// Fq is a classless packet scheduler meant to be mostly used for locally generated traffic.
269type Fq struct {
270	QdiscAttrs
271	PacketLimit     uint32
272	FlowPacketLimit uint32
273	// In bytes
274	Quantum        uint32
275	InitialQuantum uint32
276	// called RateEnable under the hood
277	Pacing          uint32
278	FlowDefaultRate uint32
279	FlowMaxRate     uint32
280	// called BucketsLog under the hood
281	Buckets          uint32
282	FlowRefillDelay  uint32
283	LowRateThreshold uint32
284}
285
286func (fq *Fq) String() string {
287	return fmt.Sprintf(
288		"{PacketLimit: %v, FlowPacketLimit: %v, Quantum: %v, InitialQuantum: %v, Pacing: %v, FlowDefaultRate: %v, FlowMaxRate: %v, Buckets: %v, FlowRefillDelay: %v,  LowRateThreshold: %v}",
289		fq.PacketLimit, fq.FlowPacketLimit, fq.Quantum, fq.InitialQuantum, fq.Pacing, fq.FlowDefaultRate, fq.FlowMaxRate, fq.Buckets, fq.FlowRefillDelay, fq.LowRateThreshold,
290	)
291}
292
293func NewFq(attrs QdiscAttrs) *Fq {
294	return &Fq{
295		QdiscAttrs: attrs,
296		Pacing:     1,
297	}
298}
299
300func (qdisc *Fq) Attrs() *QdiscAttrs {
301	return &qdisc.QdiscAttrs
302}
303
304func (qdisc *Fq) Type() string {
305	return "fq"
306}
307
308// FQ_Codel (Fair Queuing Controlled Delay) is queuing discipline that combines Fair Queuing with the CoDel AQM scheme.
309type FqCodel struct {
310	QdiscAttrs
311	Target   uint32
312	Limit    uint32
313	Interval uint32
314	ECN      uint32
315	Flows    uint32
316	Quantum  uint32
317	// There are some more attributes here, but support for them seems not ubiquitous
318}
319
320func (fqcodel *FqCodel) String() string {
321	return fmt.Sprintf(
322		"{%v -- Target: %v, Limit: %v, Interval: %v, ECM: %v, Flows: %v, Quantum: %v}",
323		fqcodel.Attrs(), fqcodel.Target, fqcodel.Limit, fqcodel.Interval, fqcodel.ECN, fqcodel.Flows, fqcodel.Quantum,
324	)
325}
326
327func NewFqCodel(attrs QdiscAttrs) *FqCodel {
328	return &FqCodel{
329		QdiscAttrs: attrs,
330		ECN:        1,
331	}
332}
333
334func (qdisc *FqCodel) Attrs() *QdiscAttrs {
335	return &qdisc.QdiscAttrs
336}
337
338func (qdisc *FqCodel) Type() string {
339	return "fq_codel"
340}
341