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