github.com/vishvananda/netlink@v1.3.0/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 Rate64: 10 * 1024 * 1024, 158 } 159 qdiscnetem := NewNetem(qattrs, nattrs) 160 if err := QdiscAdd(qdiscnetem); err != nil { 161 t.Fatal(err) 162 } 163 164 qdiscs, err = SafeQdiscList(link) 165 if err != nil { 166 t.Fatal(err) 167 } 168 if len(qdiscs) != 2 { 169 t.Fatal("Failed to add qdisc") 170 } 171 _, ok = qdiscs[0].(*Htb) 172 if !ok { 173 t.Fatal("Qdisc is the wrong type") 174 } 175 176 netem, ok := qdiscs[1].(*Netem) 177 if !ok { 178 t.Fatal("Qdisc is the wrong type") 179 } 180 // Compare the record we got from the list with the one we created 181 if netem.Loss != qdiscnetem.Loss { 182 t.Fatal("Loss does not match") 183 } 184 if netem.Latency != qdiscnetem.Latency { 185 t.Fatal("Latency does not match") 186 } 187 if netem.CorruptProb != qdiscnetem.CorruptProb { 188 t.Fatal("CorruptProb does not match") 189 } 190 if netem.Jitter != qdiscnetem.Jitter { 191 t.Fatal("Jitter does not match") 192 } 193 if netem.LossCorr != qdiscnetem.LossCorr { 194 t.Fatal("Loss does not match") 195 } 196 if netem.DuplicateCorr != qdiscnetem.DuplicateCorr { 197 t.Fatal("DuplicateCorr does not match") 198 } 199 if netem.Rate64 != qdiscnetem.Rate64 { 200 t.Fatalf("Rate64 does not match. Expected %d, got %d", netem.Rate64, qdiscnetem.Rate64) 201 } 202 203 // Deletion 204 // automatically removes netem qdisc 205 if err := ClassDel(class); err != nil { 206 t.Fatal(err) 207 } 208 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 209 if err != nil { 210 t.Fatal(err) 211 } 212 if len(classes) != 0 { 213 t.Fatal("Failed to remove class") 214 } 215 if err := QdiscDel(qdisc); err != nil { 216 t.Fatal(err) 217 } 218 qdiscs, err = SafeQdiscList(link) 219 if err != nil { 220 t.Fatal(err) 221 } 222 if len(qdiscs) != 0 { 223 t.Fatal("Failed to remove qdisc") 224 } 225 } 226 227 func TestHtbClassAddHtbClassChangeDel(t *testing.T) { 228 /** 229 This test first set up a interface ans set up a Htb qdisc 230 A HTB class is attach to it and a Netem qdisc is attached to that class 231 Next, we test changing the HTB class in place and confirming the Netem is 232 still attached. We also check that invoting ClassChange with a non-existing 233 class will fail. 234 Finally, we test ClassReplace. We confirm it correctly behave like 235 ClassChange when the parent/handle pair exists and that it will create a 236 new class if the handle is modified. 237 */ 238 tearDown := setUpNetlinkTest(t) 239 defer tearDown() 240 if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { 241 t.Fatal(err) 242 } 243 link, err := LinkByName("foo") 244 if err != nil { 245 t.Fatal(err) 246 } 247 if err := LinkSetUp(link); err != nil { 248 t.Fatal(err) 249 } 250 attrs := QdiscAttrs{ 251 LinkIndex: link.Attrs().Index, 252 Handle: MakeHandle(0xffff, 0), 253 Parent: HANDLE_ROOT, 254 } 255 qdisc := NewHtb(attrs) 256 if err := QdiscAdd(qdisc); err != nil { 257 t.Fatal(err) 258 } 259 qdiscs, err := SafeQdiscList(link) 260 if err != nil { 261 t.Fatal(err) 262 } 263 if len(qdiscs) != 1 { 264 t.Fatal("Failed to add qdisc") 265 } 266 _, ok := qdiscs[0].(*Htb) 267 if !ok { 268 t.Fatal("Qdisc is the wrong type") 269 } 270 271 classattrs := ClassAttrs{ 272 LinkIndex: link.Attrs().Index, 273 Parent: MakeHandle(0xffff, 0), 274 Handle: MakeHandle(0xffff, 2), 275 } 276 277 htbclassattrs := HtbClassAttrs{ 278 Rate: uint64(1<<32) + 10, 279 Ceil: uint64(1<<32) + 20, 280 Cbuffer: 1690, 281 } 282 class := NewHtbClass(classattrs, htbclassattrs) 283 if err := ClassAdd(class); err != nil { 284 t.Fatal(err) 285 } 286 classes, err := SafeClassList(link, 0) 287 if err != nil { 288 t.Fatal(err) 289 } 290 if len(classes) != 1 { 291 t.Fatal("Failed to add class") 292 } 293 294 htb, ok := classes[0].(*HtbClass) 295 if !ok { 296 t.Fatal("Class is the wrong type") 297 } 298 299 testClassStats(htb.ClassAttrs.Statistics, NewClassStatistics(), t) 300 301 qattrs := QdiscAttrs{ 302 LinkIndex: link.Attrs().Index, 303 Handle: MakeHandle(0x2, 0), 304 Parent: MakeHandle(0xffff, 2), 305 } 306 nattrs := NetemQdiscAttrs{ 307 Latency: 20000, 308 Loss: 23.4, 309 Duplicate: 14.3, 310 LossCorr: 8.34, 311 Jitter: 1000, 312 DelayCorr: 12.3, 313 ReorderProb: 23.4, 314 CorruptProb: 10.0, 315 CorruptCorr: 10, 316 } 317 qdiscnetem := NewNetem(qattrs, nattrs) 318 if err := QdiscAdd(qdiscnetem); err != nil { 319 t.Fatal(err) 320 } 321 322 qdiscs, err = SafeQdiscList(link) 323 if err != nil { 324 t.Fatal(err) 325 } 326 if len(qdiscs) != 2 { 327 t.Fatal("Failed to add qdisc") 328 } 329 330 _, ok = qdiscs[1].(*Netem) 331 if !ok { 332 t.Fatal("Qdisc is the wrong type") 333 } 334 335 // Change 336 // For change to work, the handle and parent cannot be changed. 337 338 // First, test it fails if we change the Handle. 339 oldHandle := classattrs.Handle 340 classattrs.Handle = MakeHandle(0xffff, 3) 341 class = NewHtbClass(classattrs, htbclassattrs) 342 if err := ClassChange(class); err == nil { 343 t.Fatal("ClassChange should not work when using a different handle.") 344 } 345 // It should work with the same handle 346 classattrs.Handle = oldHandle 347 htbclassattrs.Rate = 4321000 348 class = NewHtbClass(classattrs, htbclassattrs) 349 if err := ClassChange(class); err != nil { 350 t.Fatal(err) 351 } 352 353 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 354 if err != nil { 355 t.Fatal(err) 356 } 357 if len(classes) != 1 { 358 t.Fatalf( 359 "1 class expected, %d found", 360 len(classes), 361 ) 362 } 363 364 htb, ok = classes[0].(*HtbClass) 365 if !ok { 366 t.Fatal("Class is the wrong type") 367 } 368 // Verify that the rate value has changed. 369 if htb.Rate != class.Rate { 370 t.Fatal("Rate did not get changed while changing the class.") 371 } 372 373 // Check that we still have the netem child qdisc 374 qdiscs, err = SafeQdiscList(link) 375 if err != nil { 376 t.Fatal(err) 377 } 378 379 if len(qdiscs) != 2 { 380 t.Fatalf("2 qdisc expected, %d found", len(qdiscs)) 381 } 382 _, ok = qdiscs[0].(*Htb) 383 if !ok { 384 t.Fatal("Qdisc is the wrong type") 385 } 386 387 _, ok = qdiscs[1].(*Netem) 388 if !ok { 389 t.Fatal("Qdisc is the wrong type") 390 } 391 392 // Replace 393 // First replace by keeping the same handle, class will be changed. 394 // Then, replace by providing a new handle, n new class will be created. 395 396 // Replace acting as Change 397 class = NewHtbClass(classattrs, htbclassattrs) 398 if err := ClassReplace(class); err != nil { 399 t.Fatal("Failed to replace class that is existing.") 400 } 401 402 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 403 if err != nil { 404 t.Fatal(err) 405 } 406 if len(classes) != 1 { 407 t.Fatalf( 408 "1 class expected, %d found", 409 len(classes), 410 ) 411 } 412 413 htb, ok = classes[0].(*HtbClass) 414 if !ok { 415 t.Fatal("Class is the wrong type") 416 } 417 // Verify that the rate value has changed. 418 if htb.Rate != class.Rate { 419 t.Fatal("Rate did not get changed while changing the class.") 420 } 421 422 // It should work with the same handle 423 classattrs.Handle = MakeHandle(0xffff, 3) 424 class = NewHtbClass(classattrs, htbclassattrs) 425 if err := ClassReplace(class); err != nil { 426 t.Fatal(err) 427 } 428 429 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 430 if err != nil { 431 t.Fatal(err) 432 } 433 if len(classes) != 2 { 434 t.Fatalf( 435 "2 classes expected, %d found", 436 len(classes), 437 ) 438 } 439 440 htb, ok = classes[1].(*HtbClass) 441 if !ok { 442 t.Fatal("Class is the wrong type") 443 } 444 // Verify that the rate value has changed. 445 if htb.Rate != class.Rate { 446 t.Fatal("Rate did not get changed while changing the class.") 447 } 448 449 // Deletion 450 for _, class := range classes { 451 if err := ClassDel(class); err != nil { 452 t.Fatal(err) 453 } 454 } 455 456 classes, err = SafeClassList(link, MakeHandle(0xffff, 0)) 457 if err != nil { 458 t.Fatal(err) 459 } 460 if len(classes) != 0 { 461 t.Fatal("Failed to remove class") 462 } 463 if err := QdiscDel(qdisc); err != nil { 464 t.Fatal(err) 465 } 466 qdiscs, err = SafeQdiscList(link) 467 if err != nil { 468 t.Fatal(err) 469 } 470 if len(qdiscs) != 0 { 471 t.Fatal("Failed to remove qdisc") 472 } 473 } 474 475 func TestClassHfsc(t *testing.T) { 476 // New network namespace for tests 477 tearDown := setUpNetlinkTestWithKModule(t, "sch_hfsc") 478 defer tearDown() 479 480 // Set up testing link and check if succeeded 481 if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { 482 t.Fatal(err) 483 } 484 link, err := LinkByName("foo") 485 if err != nil { 486 t.Fatal(err) 487 } 488 if err := LinkSetUp(link); err != nil { 489 t.Fatal(err) 490 } 491 492 // Adding HFSC qdisc 493 qdiscAttrs := QdiscAttrs{ 494 LinkIndex: link.Attrs().Index, 495 Handle: MakeHandle(1, 0), 496 Parent: HANDLE_ROOT, 497 } 498 hfscQdisc := NewHfsc(qdiscAttrs) 499 hfscQdisc.Defcls = 2 500 501 err = QdiscAdd(hfscQdisc) 502 if err != nil { 503 t.Fatal(err) 504 } 505 qdiscs, err := SafeQdiscList(link) 506 if err != nil { 507 t.Fatal(err) 508 } 509 if len(qdiscs) != 1 { 510 t.Fatal("Failed to add qdisc") 511 } 512 _, ok := qdiscs[0].(*Hfsc) 513 if !ok { 514 t.Fatal("Qdisc is the wrong type") 515 } 516 517 // Adding some HFSC classes 518 classAttrs := ClassAttrs{ 519 LinkIndex: link.Attrs().Index, 520 Parent: MakeHandle(1, 0), 521 Handle: MakeHandle(1, 1), 522 } 523 hfscClass := NewHfscClass(classAttrs) 524 hfscClass.SetLS(5e6, 10, 5e6) 525 526 err = ClassAdd(hfscClass) 527 if err != nil { 528 t.Fatal(err) 529 } 530 531 hfscClass2 := hfscClass 532 hfscClass2.SetLS(0, 0, 0) 533 hfscClass2.Attrs().Parent = MakeHandle(1, 1) 534 hfscClass2.Attrs().Handle = MakeHandle(1, 2) 535 hfscClass2.SetRsc(0, 0, 2e6) 536 537 err = ClassAdd(hfscClass2) 538 if err != nil { 539 t.Fatal(err) 540 } 541 542 hfscClass3 := hfscClass 543 hfscClass3.SetLS(0, 0, 0) 544 hfscClass3.Attrs().Parent = MakeHandle(1, 1) 545 hfscClass3.Attrs().Handle = MakeHandle(1, 3) 546 547 err = ClassAdd(hfscClass3) 548 if err != nil { 549 t.Fatal(err) 550 } 551 552 // Check the classes 553 classes, err := SafeClassList(link, MakeHandle(1, 0)) 554 if err != nil { 555 t.Fatal(err) 556 } 557 if len(classes) != 4 { 558 t.Fatal("Failed to add classes") 559 } 560 for _, c := range classes { 561 class, ok := c.(*HfscClass) 562 if !ok { 563 t.Fatal("Wrong type of class") 564 } 565 if class.ClassAttrs.Handle == hfscClass.ClassAttrs.Handle { 566 if class.Fsc != hfscClass.Fsc { 567 t.Fatal("HfscClass FSC don't match") 568 } 569 if class.Usc != hfscClass.Usc { 570 t.Fatal("HfscClass USC don't match") 571 } 572 if class.Rsc != hfscClass.Rsc { 573 t.Fatal("HfscClass RSC don't match") 574 } 575 } 576 if class.ClassAttrs.Handle == hfscClass2.ClassAttrs.Handle { 577 if class.Fsc != hfscClass2.Fsc { 578 t.Fatal("HfscClass2 FSC don't match") 579 } 580 if class.Usc != hfscClass2.Usc { 581 t.Fatal("HfscClass2 USC don't match") 582 } 583 if class.Rsc != hfscClass2.Rsc { 584 t.Fatal("HfscClass2 RSC don't match") 585 } 586 } 587 if class.ClassAttrs.Handle == hfscClass3.ClassAttrs.Handle { 588 if class.Fsc != hfscClass3.Fsc { 589 t.Fatal("HfscClass3 FSC don't match") 590 } 591 if class.Usc != hfscClass3.Usc { 592 t.Fatal("HfscClass3 USC don't match") 593 } 594 if class.Rsc != hfscClass3.Rsc { 595 t.Fatal("HfscClass3 RSC don't match") 596 } 597 } 598 } 599 600 // Terminating the leafs with fq_codel qdiscs 601 fqcodelAttrs := QdiscAttrs{ 602 LinkIndex: link.Attrs().Index, 603 Parent: MakeHandle(1, 2), 604 Handle: MakeHandle(2, 0), 605 } 606 fqcodel1 := NewFqCodel(fqcodelAttrs) 607 fqcodel1.ECN = 0 608 fqcodel1.Limit = 1200 609 fqcodel1.Flows = 65535 610 fqcodel1.Target = 5 611 612 err = QdiscAdd(fqcodel1) 613 if err != nil { 614 t.Fatal(err) 615 } 616 617 fqcodel2 := fqcodel1 618 fqcodel2.Attrs().Handle = MakeHandle(3, 0) 619 fqcodel2.Attrs().Parent = MakeHandle(1, 3) 620 621 err = QdiscAdd(fqcodel2) 622 if err != nil { 623 t.Fatal(err) 624 } 625 626 // Check the amount of qdiscs 627 qdiscs, _ = SafeQdiscList(link) 628 if len(qdiscs) != 3 { 629 t.Fatal("Failed to add qdisc") 630 } 631 for _, q := range qdiscs[1:] { 632 _, ok = q.(*FqCodel) 633 if !ok { 634 t.Fatal("Qdisc is the wrong type") 635 } 636 } 637 638 // removing a class 639 if err := ClassDel(hfscClass3); err != nil { 640 t.Fatal(err) 641 } 642 // Check the classes 643 classes, err = SafeClassList(link, MakeHandle(1, 0)) 644 if err != nil { 645 t.Fatal(err) 646 } 647 if len(classes) != 3 { 648 t.Fatal("Failed to delete classes") 649 } 650 // Check qdisc 651 qdiscs, _ = SafeQdiscList(link) 652 if len(qdiscs) != 2 { 653 t.Fatal("Failed to delete qdisc") 654 } 655 656 // Changing a class 657 hfscClass2.SetRsc(0, 0, 0) 658 hfscClass2.SetSC(5e6, 100, 1e6) 659 hfscClass2.SetUL(6e6, 50, 2e6) 660 hfscClass2.Attrs().Handle = MakeHandle(1, 8) 661 if err := ClassChange(hfscClass2); err == nil { 662 t.Fatal("Class change shouldn't work with a different handle") 663 } 664 hfscClass2.Attrs().Handle = MakeHandle(1, 2) 665 if err := ClassChange(hfscClass2); err != nil { 666 t.Fatal(err) 667 } 668 669 // Replacing a class 670 // If the handle doesn't exist, create it 671 hfscClass2.SetSC(6e6, 100, 2e6) 672 hfscClass2.SetUL(8e6, 500, 4e6) 673 hfscClass2.Attrs().Handle = MakeHandle(1, 8) 674 if err := ClassReplace(hfscClass2); err != nil { 675 t.Fatal(err) 676 } 677 // If the handle exists, replace it 678 hfscClass.SetLS(5e6, 200, 1e6) 679 if err := ClassChange(hfscClass); err != nil { 680 t.Fatal(err) 681 } 682 683 }