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