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