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