1 package netlink
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "encoding/hex"
7 "errors"
8 "fmt"
9 "syscall"
10
11 "github.com/vishvananda/netlink/nl"
12 "golang.org/x/sys/unix"
13 )
14
15
16
17
18
19
20 type tcStats struct {
21 Bytes uint64
22 Packets uint32
23 Drops uint32
24 Overlimits uint32
25 Bps uint32
26 Pps uint32
27 Qlen uint32
28 Backlog uint32
29 }
30
31
32 func NewHtbClass(attrs ClassAttrs, cattrs HtbClassAttrs) *HtbClass {
33 mtu := 1600
34 rate := cattrs.Rate / 8
35 ceil := cattrs.Ceil / 8
36 buffer := cattrs.Buffer
37 cbuffer := cattrs.Cbuffer
38
39 if ceil == 0 {
40 ceil = rate
41 }
42
43 if buffer == 0 {
44 buffer = uint32(float64(rate)/Hz() + float64(mtu))
45 }
46 buffer = Xmittime(rate, buffer)
47
48 if cbuffer == 0 {
49 cbuffer = uint32(float64(ceil)/Hz() + float64(mtu))
50 }
51 cbuffer = Xmittime(ceil, cbuffer)
52
53 return &HtbClass{
54 ClassAttrs: attrs,
55 Rate: rate,
56 Ceil: ceil,
57 Buffer: buffer,
58 Cbuffer: cbuffer,
59 Level: 0,
60 Prio: cattrs.Prio,
61 Quantum: cattrs.Quantum,
62 }
63 }
64
65
66
67 func ClassDel(class Class) error {
68 return pkgHandle.ClassDel(class)
69 }
70
71
72
73 func (h *Handle) ClassDel(class Class) error {
74 return h.classModify(unix.RTM_DELTCLASS, 0, class)
75 }
76
77
78
79
80 func ClassChange(class Class) error {
81 return pkgHandle.ClassChange(class)
82 }
83
84
85
86
87 func (h *Handle) ClassChange(class Class) error {
88 return h.classModify(unix.RTM_NEWTCLASS, 0, class)
89 }
90
91
92
93
94
95
96 func ClassReplace(class Class) error {
97 return pkgHandle.ClassReplace(class)
98 }
99
100
101
102
103
104
105 func (h *Handle) ClassReplace(class Class) error {
106 return h.classModify(unix.RTM_NEWTCLASS, unix.NLM_F_CREATE, class)
107 }
108
109
110
111 func ClassAdd(class Class) error {
112 return pkgHandle.ClassAdd(class)
113 }
114
115
116
117 func (h *Handle) ClassAdd(class Class) error {
118 return h.classModify(
119 unix.RTM_NEWTCLASS,
120 unix.NLM_F_CREATE|unix.NLM_F_EXCL,
121 class,
122 )
123 }
124
125 func (h *Handle) classModify(cmd, flags int, class Class) error {
126 req := h.newNetlinkRequest(cmd, flags|unix.NLM_F_ACK)
127 base := class.Attrs()
128 msg := &nl.TcMsg{
129 Family: nl.FAMILY_ALL,
130 Ifindex: int32(base.LinkIndex),
131 Handle: base.Handle,
132 Parent: base.Parent,
133 }
134 req.AddData(msg)
135
136 if cmd != unix.RTM_DELTCLASS {
137 if err := classPayload(req, class); err != nil {
138 return err
139 }
140 }
141 _, err := req.Execute(unix.NETLINK_ROUTE, 0)
142 return err
143 }
144
145 func classPayload(req *nl.NetlinkRequest, class Class) error {
146 req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
147
148 options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
149 switch class.Type() {
150 case "htb":
151 htb := class.(*HtbClass)
152 opt := nl.TcHtbCopt{}
153 opt.Buffer = htb.Buffer
154 opt.Cbuffer = htb.Cbuffer
155 opt.Quantum = htb.Quantum
156 opt.Level = htb.Level
157 opt.Prio = htb.Prio
158
159
160 cellLog := -1
161 ccellLog := -1
162 linklayer := nl.LINKLAYER_ETHERNET
163 mtu := 1600
164 var rtab [256]uint32
165 var ctab [256]uint32
166 tcrate := nl.TcRateSpec{Rate: uint32(htb.Rate)}
167 if CalcRtable(&tcrate, rtab[:], cellLog, uint32(mtu), linklayer) < 0 {
168 return errors.New("HTB: failed to calculate rate table")
169 }
170 opt.Rate = tcrate
171 tcceil := nl.TcRateSpec{Rate: uint32(htb.Ceil)}
172 if CalcRtable(&tcceil, ctab[:], ccellLog, uint32(mtu), linklayer) < 0 {
173 return errors.New("HTB: failed to calculate ceil rate table")
174 }
175 opt.Ceil = tcceil
176 options.AddRtAttr(nl.TCA_HTB_PARMS, opt.Serialize())
177 options.AddRtAttr(nl.TCA_HTB_RTAB, SerializeRtab(rtab))
178 options.AddRtAttr(nl.TCA_HTB_CTAB, SerializeRtab(ctab))
179 if htb.Rate >= uint64(1<<32) {
180 options.AddRtAttr(nl.TCA_HTB_RATE64, nl.Uint64Attr(htb.Rate))
181 }
182 if htb.Ceil >= uint64(1<<32) {
183 options.AddRtAttr(nl.TCA_HTB_CEIL64, nl.Uint64Attr(htb.Ceil))
184 }
185 case "hfsc":
186 hfsc := class.(*HfscClass)
187 opt := nl.HfscCopt{}
188 rm1, rd, rm2 := hfsc.Rsc.Attrs()
189 opt.Rsc.Set(rm1/8, rd, rm2/8)
190 fm1, fd, fm2 := hfsc.Fsc.Attrs()
191 opt.Fsc.Set(fm1/8, fd, fm2/8)
192 um1, ud, um2 := hfsc.Usc.Attrs()
193 opt.Usc.Set(um1/8, ud, um2/8)
194 options.AddRtAttr(nl.TCA_HFSC_RSC, nl.SerializeHfscCurve(&opt.Rsc))
195 options.AddRtAttr(nl.TCA_HFSC_FSC, nl.SerializeHfscCurve(&opt.Fsc))
196 options.AddRtAttr(nl.TCA_HFSC_USC, nl.SerializeHfscCurve(&opt.Usc))
197 }
198 req.AddData(options)
199 return nil
200 }
201
202
203
204
205 func ClassList(link Link, parent uint32) ([]Class, error) {
206 return pkgHandle.ClassList(link, parent)
207 }
208
209
210
211
212 func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
213 req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
214 msg := &nl.TcMsg{
215 Family: nl.FAMILY_ALL,
216 Parent: parent,
217 }
218 if link != nil {
219 base := link.Attrs()
220 h.ensureIndex(base)
221 msg.Ifindex = int32(base.Index)
222 }
223 req.AddData(msg)
224
225 msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
226 if err != nil {
227 return nil, err
228 }
229
230 var res []Class
231 for _, m := range msgs {
232 msg := nl.DeserializeTcMsg(m)
233
234 attrs, err := nl.ParseRouteAttr(m[msg.Len():])
235 if err != nil {
236 return nil, err
237 }
238
239 base := ClassAttrs{
240 LinkIndex: int(msg.Ifindex),
241 Handle: msg.Handle,
242 Parent: msg.Parent,
243 Statistics: nil,
244 }
245
246 var class Class
247 classType := ""
248 for _, attr := range attrs {
249 switch attr.Attr.Type {
250 case nl.TCA_KIND:
251 classType = string(attr.Value[:len(attr.Value)-1])
252 switch classType {
253 case "htb":
254 class = &HtbClass{}
255 case "hfsc":
256 class = &HfscClass{}
257 default:
258 class = &GenericClass{ClassType: classType}
259 }
260 case nl.TCA_OPTIONS:
261 switch classType {
262 case "htb":
263 data, err := nl.ParseRouteAttr(attr.Value)
264 if err != nil {
265 return nil, err
266 }
267 _, err = parseHtbClassData(class, data)
268 if err != nil {
269 return nil, err
270 }
271 case "hfsc":
272 data, err := nl.ParseRouteAttr(attr.Value)
273 if err != nil {
274 return nil, err
275 }
276 _, err = parseHfscClassData(class, data)
277 if err != nil {
278 return nil, err
279 }
280 }
281
282 case nl.TCA_STATS:
283 base.Statistics, err = parseTcStats(attr.Value)
284 if err != nil {
285 return nil, err
286 }
287 case nl.TCA_STATS2:
288 base.Statistics, err = parseTcStats2(attr.Value)
289 if err != nil {
290 return nil, err
291 }
292 }
293 }
294 *class.Attrs() = base
295 res = append(res, class)
296 }
297
298 return res, nil
299 }
300
301 func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
302 htb := class.(*HtbClass)
303 detailed := false
304 for _, datum := range data {
305 switch datum.Attr.Type {
306 case nl.TCA_HTB_PARMS:
307 opt := nl.DeserializeTcHtbCopt(datum.Value)
308 htb.Rate = uint64(opt.Rate.Rate)
309 htb.Ceil = uint64(opt.Ceil.Rate)
310 htb.Buffer = opt.Buffer
311 htb.Cbuffer = opt.Cbuffer
312 htb.Quantum = opt.Quantum
313 htb.Level = opt.Level
314 htb.Prio = opt.Prio
315 case nl.TCA_HTB_RATE64:
316 htb.Rate = native.Uint64(datum.Value[0:8])
317 case nl.TCA_HTB_CEIL64:
318 htb.Ceil = native.Uint64(datum.Value[0:8])
319 }
320 }
321 return detailed, nil
322 }
323
324 func parseHfscClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
325 hfsc := class.(*HfscClass)
326 detailed := false
327 for _, datum := range data {
328 m1, d, m2 := nl.DeserializeHfscCurve(datum.Value).Attrs()
329 switch datum.Attr.Type {
330 case nl.TCA_HFSC_RSC:
331 hfsc.Rsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
332 case nl.TCA_HFSC_FSC:
333 hfsc.Fsc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
334 case nl.TCA_HFSC_USC:
335 hfsc.Usc = ServiceCurve{m1: m1 * 8, d: d, m2: m2 * 8}
336 }
337 }
338 return detailed, nil
339 }
340
341 func parseTcStats(data []byte) (*ClassStatistics, error) {
342 buf := &bytes.Buffer{}
343 buf.Write(data)
344 tcStats := &tcStats{}
345 if err := binary.Read(buf, native, tcStats); err != nil {
346 return nil, err
347 }
348
349 stats := NewClassStatistics()
350 stats.Basic.Bytes = tcStats.Bytes
351 stats.Basic.Packets = tcStats.Packets
352 stats.Queue.Qlen = tcStats.Qlen
353 stats.Queue.Backlog = tcStats.Backlog
354 stats.Queue.Drops = tcStats.Drops
355 stats.Queue.Overlimits = tcStats.Overlimits
356 stats.RateEst.Bps = tcStats.Bps
357 stats.RateEst.Pps = tcStats.Pps
358
359 return stats, nil
360 }
361
362 func parseGnetStats(data []byte, gnetStats interface{}) error {
363 buf := &bytes.Buffer{}
364 buf.Write(data)
365 return binary.Read(buf, native, gnetStats)
366 }
367
368 func parseTcStats2(data []byte) (*ClassStatistics, error) {
369 rtAttrs, err := nl.ParseRouteAttr(data)
370 if err != nil {
371 return nil, err
372 }
373 stats := NewClassStatistics()
374 for _, datum := range rtAttrs {
375 switch datum.Attr.Type {
376 case nl.TCA_STATS_BASIC:
377 if err := parseGnetStats(datum.Value, stats.Basic); err != nil {
378 return nil, fmt.Errorf("Failed to parse ClassStatistics.Basic with: %v\n%s",
379 err, hex.Dump(datum.Value))
380 }
381 case nl.TCA_STATS_QUEUE:
382 if err := parseGnetStats(datum.Value, stats.Queue); err != nil {
383 return nil, fmt.Errorf("Failed to parse ClassStatistics.Queue with: %v\n%s",
384 err, hex.Dump(datum.Value))
385 }
386 case nl.TCA_STATS_RATE_EST:
387 if err := parseGnetStats(datum.Value, stats.RateEst); err != nil {
388 return nil, fmt.Errorf("Failed to parse ClassStatistics.RateEst with: %v\n%s",
389 err, hex.Dump(datum.Value))
390 }
391 case nl.TCA_STATS_BASIC_HW:
392 if err := parseGnetStats(datum.Value, stats.BasicHw); err != nil {
393 return nil, fmt.Errorf("Failed to parse ClassStatistics.BasicHw with: %v\n%s",
394 err, hex.Dump(datum.Value))
395 }
396 }
397 }
398
399 return stats, nil
400 }
401
View as plain text