github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/class_test.go (about) 1 //go:build linux 2 // +build linux 3 4 package netlink 5 6 import ( 7 "reflect" 8 "testing" 9 ) 10 11 func SafeQdiscList(link Link) ([]Qdisc, error) { 12 qdiscs, err := QdiscList(link) 13 if err != nil { 14 return nil, err 15 } 16 result := []Qdisc{} 17 for _, qdisc := range qdiscs { 18 // fmt.Printf("%+v\n", qdisc) 19 // filter default qdisc created by kernel when custom one deleted 20 attrs := qdisc.Attrs() 21 if attrs.Handle == HANDLE_NONE && attrs.Parent == HANDLE_ROOT { 22 continue 23 } 24 result = append(result, qdisc) 25 } 26 return result, nil 27 } 28 29 func SafeClassList(link Link, handle uint32) ([]Class, error) { 30 classes, err := ClassList(link, handle) 31 if err != nil { 32 return nil, err 33 } 34 result := []Class{} 35 for ind := range classes { 36 double := false 37 for _, class2 := range classes[ind+1:] { 38 if classes[ind].Attrs().Handle == class2.Attrs().Handle { 39 double = true 40 } 41 } 42 if !double { 43 result = append(result, classes[ind]) 44 } 45 } 46 return result, nil 47 } 48 49 func testClassStats(this, that *ClassStatistics, t *testing.T) { 50 ok := reflect.DeepEqual(this, that) 51 if !ok { 52 t.Fatalf("%#v is expected but it actually was %#v", that, this) 53 } 54 } 55 56 func TestClassAddDel(t *testing.T) { 57 tearDown := setUpNetlinkTest(t) 58 defer tearDown() 59 if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { 60 t.Fatal(err) 61 } 62 if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil { 63 t.Fatal(err) 64 } 65 link, err := LinkByName("foo") 66 if err != nil { 67 t.Fatal(err) 68 } 69 if err := LinkSetUp(link); err != nil { 70 t.Fatal(err) 71 } 72 attrs := QdiscAttrs{ 73 LinkIndex: link.Attrs().Index, 74 Handle: MakeHandle(0xffff, 0), 75 Parent: HANDLE_ROOT, 76 } 77 qdisc := NewHtb(attrs) 78 if err := QdiscAdd(qdisc); err != nil { 79 t.Fatal(err) 80 } 81 qdiscs, err := SafeQdiscList(link) 82 if err != nil { 83 t.Fatal(err) 84 } 85 if len(qdiscs) != 1 { 86 t.Fatal("Failed to add qdisc") 87 } 88 _, ok := qdiscs[0].(*Htb) 89 if !ok { 90 t.Fatal("Qdisc is the wrong type") 91 } 92 93 classattrs := ClassAttrs{ 94 LinkIndex: link.Attrs().Index, 95 Parent: MakeHandle(0xffff, 0), 96 Handle: MakeHandle(0xffff, 2), 97 } 98 99 htbclassattrs := HtbClassAttrs{ 100 Rate: 1234000, 101 Cbuffer: 1690, 102 Prio: 2, 103 Quantum: 1000, 104 } 105 class := NewHtbClass(classattrs, htbclassattrs) 106 if err := ClassAdd(class); err != nil { 107 t.Fatal(err) 108 } 109 classes, err := SafeClassList(link, MakeHandle(0xffff, 0)) 110 if err != nil { 111 t.Fatal(err) 112 } 113 if len(classes) != 1 { 114 t.Fatal("Failed to add class") 115 } 116 117 htb, ok := classes[0].(*HtbClass) 118 if !ok { 119 t.Fatal("Class is the wrong type") 120 } 121 if htb.Rate != class.Rate { 122 t.Fatal("Rate doesn't match") 123 } 124 if htb.Ceil != class.Ceil { 125 t.Fatal("Ceil doesn't match") 126 } 127 if htb.Buffer != class.Buffer { 128 t.Fatal("Buffer doesn't match") 129 } 130 if htb.Cbuffer != class.Cbuffer { 131 t.Fatal("Cbuffer doesn't match") 132 } 133 if htb.Prio != class.Prio { 134 t.Fatal("Prio doesn't match") 135 } 136 if htb.Quantum != class.Quantum { 137 t.Fatal("Quantum doesn't match") 138 } 139 140 testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t) 141 142 qattrs := QdiscAttrs{ 143 LinkIndex: link.Attrs().Index, 144 Handle: MakeHandle(0x2, 0), 145 Parent: MakeHandle(0xffff, 2), 146 } 147 nattrs := NetemQdiscAttrs{ 148 Latency: 20000, 149 Loss: 23.4, 150 Duplicate: 14.3, 151 LossCorr: 8.34, 152 Jitter: 1000, 153 DelayCorr: 12.3, 154 ReorderProb: 23.4, 155 CorruptProb: 10.0, 156 CorruptCorr: 10, 157 } 158 qdiscnetem := NewNetem(qattrs, nattrs) 159 if err := QdiscAdd(qdiscnetem); err != nil { 160 t.Fatal(err) 161 } 162 163 qdiscs, err = SafeQdiscList(link) 164 if err != nil { 165 t.Fatal(err) 166 } 167 if len(qdiscs) != 2 { 168 t.Fatal("Failed to add qdisc") 169 } 170 _, ok = qdiscs[0].(*Htb) 171 if !ok { 172 t.Fatal("Qdisc is the wrong type") 173 } 174 175 netem, ok := qdiscs[1].(*Netem) 176 if !ok { 177 t.Fatal("Qdisc is the wrong type") 178 } 179 // Compare the record we got from the list with the one we created 180 if netem.Loss != qdiscnetem.Loss { 181 t.Fatal("Loss does not match") 182 } 183 if netem.Latency != qdiscnetem.Latency { 184 t.Fatal("Latency does not match") 185 } 186 if netem.CorruptProb != qdiscnetem.CorruptProb { 187 t.Fatal("CorruptProb does not match") 188 } 189 if netem.Jitter != qdiscnetem.Jitter { 190 t.Fatal("Jitter does not match") 191 } 192 if netem.LossCorr != qdiscnetem.LossCorr { 193 t.Fatal("Loss does not match") 194 } 195 if netem.DuplicateCorr != qdiscnetem.DuplicateCorr { 196 t.Fatal("DuplicateCorr does not match") 197 } 198 199 // Deletion 200 // automatically removes netem qdisc 201 if err := ClassDel(class); err != nil { 202 t.Fatal(err) 203 } 204 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 205 if err != nil { 206 t.Fatal(err) 207 } 208 if len(classes) != 0 { 209 t.Fatal("Failed to remove class") 210 } 211 if err := QdiscDel(qdisc); err != nil { 212 t.Fatal(err) 213 } 214 qdiscs, err = SafeQdiscList(link) 215 if err != nil { 216 t.Fatal(err) 217 } 218 if len(qdiscs) != 0 { 219 t.Fatal("Failed to remove qdisc") 220 } 221 } 222 223 func TestHtbClassAddHtbClassChangeDel(t *testing.T) { 224 /** 225 This test first set up a interface ans set up a Htb qdisc 226 A HTB class is attach to it and a Netem qdisc is attached to that class 227 Next, we test changing the HTB class in place and confirming the Netem is 228 still attached. We also check that invoting ClassChange with a non-existing 229 class will fail. 230 Finally, we test ClassReplace. We confirm it correctly behave like 231 ClassChange when the parent/handle pair exists and that it will create a 232 new class if the handle is modified. 233 */ 234 tearDown := setUpNetlinkTest(t) 235 defer tearDown() 236 if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { 237 t.Fatal(err) 238 } 239 link, err := LinkByName("foo") 240 if err != nil { 241 t.Fatal(err) 242 } 243 if err := LinkSetUp(link); err != nil { 244 t.Fatal(err) 245 } 246 attrs := QdiscAttrs{ 247 LinkIndex: link.Attrs().Index, 248 Handle: MakeHandle(0xffff, 0), 249 Parent: HANDLE_ROOT, 250 } 251 qdisc := NewHtb(attrs) 252 if err := QdiscAdd(qdisc); err != nil { 253 t.Fatal(err) 254 } 255 qdiscs, err := SafeQdiscList(link) 256 if err != nil { 257 t.Fatal(err) 258 } 259 if len(qdiscs) != 1 { 260 t.Fatal("Failed to add qdisc") 261 } 262 _, ok := qdiscs[0].(*Htb) 263 if !ok { 264 t.Fatal("Qdisc is the wrong type") 265 } 266 267 classattrs := ClassAttrs{ 268 LinkIndex: link.Attrs().Index, 269 Parent: MakeHandle(0xffff, 0), 270 Handle: MakeHandle(0xffff, 2), 271 } 272 273 htbclassattrs := HtbClassAttrs{ 274 Rate: uint64(1<<32) + 10, 275 Ceil: uint64(1<<32) + 20, 276 Cbuffer: 1690, 277 } 278 class := NewHtbClass(classattrs, htbclassattrs) 279 if err := ClassAdd(class); err != nil { 280 t.Fatal(err) 281 } 282 classes, err := SafeClassList(link, 0) 283 if err != nil { 284 t.Fatal(err) 285 } 286 if len(classes) != 1 { 287 t.Fatal("Failed to add class") 288 } 289 290 htb, ok := classes[0].(*HtbClass) 291 if !ok { 292 t.Fatal("Class is the wrong type") 293 } 294 295 testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t) 296 297 qattrs := QdiscAttrs{ 298 LinkIndex: link.Attrs().Index, 299 Handle: MakeHandle(0x2, 0), 300 Parent: MakeHandle(0xffff, 2), 301 } 302 nattrs := NetemQdiscAttrs{ 303 Latency: 20000, 304 Loss: 23.4, 305 Duplicate: 14.3, 306 LossCorr: 8.34, 307 Jitter: 1000, 308 DelayCorr: 12.3, 309 ReorderProb: 23.4, 310 CorruptProb: 10.0, 311 CorruptCorr: 10, 312 } 313 qdiscnetem := NewNetem(qattrs, nattrs) 314 if err := QdiscAdd(qdiscnetem); err != nil { 315 t.Fatal(err) 316 } 317 318 qdiscs, err = SafeQdiscList(link) 319 if err != nil { 320 t.Fatal(err) 321 } 322 if len(qdiscs) != 2 { 323 t.Fatal("Failed to add qdisc") 324 } 325 326 _, ok = qdiscs[1].(*Netem) 327 if !ok { 328 t.Fatal("Qdisc is the wrong type") 329 } 330 331 // Change 332 // For change to work, the handle and parent cannot be changed. 333 334 // First, test it fails if we change the Handle. 335 oldHandle := classattrs.Handle 336 classattrs.Handle = MakeHandle(0xffff, 3) 337 class = NewHtbClass(classattrs, htbclassattrs) 338 if err := ClassChange(class); err == nil { 339 t.Fatal("ClassChange should not work when using a different handle.") 340 } 341 // It should work with the same handle 342 classattrs.Handle = oldHandle 343 htbclassattrs.Rate = 4321000 344 class = NewHtbClass(classattrs, htbclassattrs) 345 if err := ClassChange(class); err != nil { 346 t.Fatal(err) 347 } 348 349 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 350 if err != nil { 351 t.Fatal(err) 352 } 353 if len(classes) != 1 { 354 t.Fatalf( 355 "1 class expected, %d found", 356 len(classes), 357 ) 358 } 359 360 htb, ok = classes[0].(*HtbClass) 361 if !ok { 362 t.Fatal("Class is the wrong type") 363 } 364 // Verify that the rate value has changed. 365 if htb.Rate != class.Rate { 366 t.Fatal("Rate did not get changed while changing the class.") 367 } 368 369 // Check that we still have the netem child qdisc 370 qdiscs, err = SafeQdiscList(link) 371 if err != nil { 372 t.Fatal(err) 373 } 374 375 if len(qdiscs) != 2 { 376 t.Fatalf("2 qdisc expected, %d found", len(qdiscs)) 377 } 378 _, ok = qdiscs[0].(*Htb) 379 if !ok { 380 t.Fatal("Qdisc is the wrong type") 381 } 382 383 _, ok = qdiscs[1].(*Netem) 384 if !ok { 385 t.Fatal("Qdisc is the wrong type") 386 } 387 388 // Replace 389 // First replace by keeping the same handle, class will be changed. 390 // Then, replace by providing a new handle, n new class will be created. 391 392 // Replace acting as Change 393 class = NewHtbClass(classattrs, htbclassattrs) 394 if err := ClassReplace(class); err != nil { 395 t.Fatal("Failed to replace class that is existing.") 396 } 397 398 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 399 if err != nil { 400 t.Fatal(err) 401 } 402 if len(classes) != 1 { 403 t.Fatalf( 404 "1 class expected, %d found", 405 len(classes), 406 ) 407 } 408 409 htb, ok = classes[0].(*HtbClass) 410 if !ok { 411 t.Fatal("Class is the wrong type") 412 } 413 // Verify that the rate value has changed. 414 if htb.Rate != class.Rate { 415 t.Fatal("Rate did not get changed while changing the class.") 416 } 417 418 // It should work with the same handle 419 classattrs.Handle = MakeHandle(0xffff, 3) 420 class = NewHtbClass(classattrs, htbclassattrs) 421 if err := ClassReplace(class); err != nil { 422 t.Fatal(err) 423 } 424 425 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 426 if err != nil { 427 t.Fatal(err) 428 } 429 if len(classes) != 2 { 430 t.Fatalf( 431 "2 classes expected, %d found", 432 len(classes), 433 ) 434 } 435 436 htb, ok = classes[1].(*HtbClass) 437 if !ok { 438 t.Fatal("Class is the wrong type") 439 } 440 // Verify that the rate value has changed. 441 if htb.Rate != class.Rate { 442 t.Fatal("Rate did not get changed while changing the class.") 443 } 444 445 // Deletion 446 for _, class := range classes { 447 if err := ClassDel(class); err != nil { 448 t.Fatal(err) 449 } 450 } 451 452 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 453 if err != nil { 454 t.Fatal(err) 455 } 456 if len(classes) != 0 { 457 t.Fatal("Failed to remove class") 458 } 459 if err := QdiscDel(qdisc); err != nil { 460 t.Fatal(err) 461 } 462 qdiscs, err = SafeQdiscList(link) 463 if err != nil { 464 t.Fatal(err) 465 } 466 if len(qdiscs) != 0 { 467 t.Fatal("Failed to remove qdisc") 468 } 469 } 470 471 func TestClassHfsc(t *testing.T) { 472 // New network namespace for tests 473 tearDown := setUpNetlinkTestWithKModule(t, "sch_hfsc") 474 defer tearDown() 475 476 // Set up testing link and check if succeeded 477 if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { 478 t.Fatal(err) 479 } 480 link, err := LinkByName("foo") 481 if err != nil { 482 t.Fatal(err) 483 } 484 if err := LinkSetUp(link); err != nil { 485 t.Fatal(err) 486 } 487 488 // Adding HFSC qdisc 489 qdiscAttrs := QdiscAttrs{ 490 LinkIndex: link.Attrs().Index, 491 Handle: MakeHandle(1, 0), 492 Parent: HANDLE_ROOT, 493 } 494 hfscQdisc := NewHfsc(qdiscAttrs) 495 hfscQdisc.Defcls = 2 496 497 err = QdiscAdd(hfscQdisc) 498 if err != nil { 499 t.Fatal(err) 500 } 501 qdiscs, err := SafeQdiscList(link) 502 if err != nil { 503 t.Fatal(err) 504 } 505 if len(qdiscs) != 1 { 506 t.Fatal("Failed to add qdisc") 507 } 508 _, ok := qdiscs[0].(*Hfsc) 509 if !ok { 510 t.Fatal("Qdisc is the wrong type") 511 } 512 513 // Adding some HFSC classes 514 classAttrs := ClassAttrs{ 515 LinkIndex: link.Attrs().Index, 516 Parent: MakeHandle(1, 0), 517 Handle: MakeHandle(1, 1), 518 } 519 hfscClass := NewHfscClass(classAttrs) 520 hfscClass.SetLS(5e6, 10, 5e6) 521 522 err = ClassAdd(hfscClass) 523 if err != nil { 524 t.Fatal(err) 525 } 526 527 hfscClass2 := hfscClass 528 hfscClass2.SetLS(0, 0, 0) 529 hfscClass2.Attrs().Parent = MakeHandle(1, 1) 530 hfscClass2.Attrs().Handle = MakeHandle(1, 2) 531 hfscClass2.SetRsc(0, 0, 2e6) 532 533 err = ClassAdd(hfscClass2) 534 if err != nil { 535 t.Fatal(err) 536 } 537 538 hfscClass3 := hfscClass 539 hfscClass3.SetLS(0, 0, 0) 540 hfscClass3.Attrs().Parent = MakeHandle(1, 1) 541 hfscClass3.Attrs().Handle = MakeHandle(1, 3) 542 543 err = ClassAdd(hfscClass3) 544 if err != nil { 545 t.Fatal(err) 546 } 547 548 // Check the classes 549 classes, err := SafeClassList(link, MakeHandle(1, 0)) 550 if err != nil { 551 t.Fatal(err) 552 } 553 if len(classes) != 4 { 554 t.Fatal("Failed to add classes") 555 } 556 for _, c := range classes { 557 class, ok := c.(*HfscClass) 558 if !ok { 559 t.Fatal("Wrong type of class") 560 } 561 if class.ClassAttrs.Handle == hfscClass.ClassAttrs.Handle { 562 if class.Fsc != hfscClass.Fsc { 563 t.Fatal("HfscClass FSC don't match") 564 } 565 if class.Usc != hfscClass.Usc { 566 t.Fatal("HfscClass USC don't match") 567 } 568 if class.Rsc != hfscClass.Rsc { 569 t.Fatal("HfscClass RSC don't match") 570 } 571 } 572 if class.ClassAttrs.Handle == hfscClass2.ClassAttrs.Handle { 573 if class.Fsc != hfscClass2.Fsc { 574 t.Fatal("HfscClass2 FSC don't match") 575 } 576 if class.Usc != hfscClass2.Usc { 577 t.Fatal("HfscClass2 USC don't match") 578 } 579 if class.Rsc != hfscClass2.Rsc { 580 t.Fatal("HfscClass2 RSC don't match") 581 } 582 } 583 if class.ClassAttrs.Handle == hfscClass3.ClassAttrs.Handle { 584 if class.Fsc != hfscClass3.Fsc { 585 t.Fatal("HfscClass3 FSC don't match") 586 } 587 if class.Usc != hfscClass3.Usc { 588 t.Fatal("HfscClass3 USC don't match") 589 } 590 if class.Rsc != hfscClass3.Rsc { 591 t.Fatal("HfscClass3 RSC don't match") 592 } 593 } 594 } 595 596 // Terminating the leafs with fq_codel qdiscs 597 fqcodelAttrs := QdiscAttrs{ 598 LinkIndex: link.Attrs().Index, 599 Parent: MakeHandle(1, 2), 600 Handle: MakeHandle(2, 0), 601 } 602 fqcodel1 := NewFqCodel(fqcodelAttrs) 603 fqcodel1.ECN = 0 604 fqcodel1.Limit = 1200 605 fqcodel1.Flows = 65535 606 fqcodel1.Target = 5 607 608 err = QdiscAdd(fqcodel1) 609 if err != nil { 610 t.Fatal(err) 611 } 612 613 fqcodel2 := fqcodel1 614 fqcodel2.Attrs().Handle = MakeHandle(3, 0) 615 fqcodel2.Attrs().Parent = MakeHandle(1, 3) 616 617 err = QdiscAdd(fqcodel2) 618 if err != nil { 619 t.Fatal(err) 620 } 621 622 // Check the amount of qdiscs 623 qdiscs, _ = SafeQdiscList(link) 624 if len(qdiscs) != 3 { 625 t.Fatal("Failed to add qdisc") 626 } 627 for _, q := range qdiscs[1:] { 628 _, ok = q.(*FqCodel) 629 if !ok { 630 t.Fatal("Qdisc is the wrong type") 631 } 632 } 633 634 // removing a class 635 if err := ClassDel(hfscClass3); err != nil { 636 t.Fatal(err) 637 } 638 // Check the classes 639 classes, err = SafeClassList(link, MakeHandle(1, 0)) 640 if err != nil { 641 t.Fatal(err) 642 } 643 if len(classes) != 3 { 644 t.Fatal("Failed to delete classes") 645 } 646 // Check qdisc 647 qdiscs, _ = SafeQdiscList(link) 648 if len(qdiscs) != 2 { 649 t.Fatal("Failed to delete qdisc") 650 } 651 652 // Changing a class 653 hfscClass2.SetRsc(0, 0, 0) 654 hfscClass2.SetSC(5e6, 100, 1e6) 655 hfscClass2.SetUL(6e6, 50, 2e6) 656 hfscClass2.Attrs().Handle = MakeHandle(1, 8) 657 if err := ClassChange(hfscClass2); err == nil { 658 t.Fatal("Class change shouldn't work with a different handle") 659 } 660 hfscClass2.Attrs().Handle = MakeHandle(1, 2) 661 if err := ClassChange(hfscClass2); err != nil { 662 t.Fatal(err) 663 } 664 665 // Replacing a class 666 // If the handle doesn't exist, create it 667 hfscClass2.SetSC(6e6, 100, 2e6) 668 hfscClass2.SetUL(8e6, 500, 4e6) 669 hfscClass2.Attrs().Handle = MakeHandle(1, 8) 670 if err := ClassReplace(hfscClass2); err != nil { 671 t.Fatal(err) 672 } 673 // If the handle exists, replace it 674 hfscClass.SetLS(5e6, 200, 1e6) 675 if err := ClassChange(hfscClass); err != nil { 676 t.Fatal(err) 677 } 678 679 }