github.com/vishvananda/netlink@v1.3.1/rule_test.go (about) 1 //go:build linux 2 // +build linux 3 4 package netlink 5 6 import ( 7 "net" 8 "testing" 9 "time" 10 11 "golang.org/x/sys/unix" 12 ) 13 14 func TestRuleAddDel(t *testing.T) { 15 skipUnlessRoot(t) 16 defer setUpNetlinkTest(t)() 17 18 srcNet := &net.IPNet{IP: net.IPv4(172, 16, 0, 1), Mask: net.CIDRMask(16, 32)} 19 dstNet := &net.IPNet{IP: net.IPv4(172, 16, 1, 1), Mask: net.CIDRMask(24, 32)} 20 21 rulesBegin, err := RuleList(FAMILY_V4) 22 if err != nil { 23 t.Fatal(err) 24 } 25 26 rule := NewRule() 27 rule.Family = FAMILY_V4 28 rule.Table = unix.RT_TABLE_MAIN 29 rule.Src = srcNet 30 rule.Dst = dstNet 31 rule.Priority = 5 32 rule.OifName = "lo" 33 rule.IifName = "lo" 34 rule.Invert = true 35 rule.Tos = 0x10 36 rule.Dport = NewRulePortRange(80, 80) 37 rule.Sport = NewRulePortRange(1000, 1024) 38 rule.IPProto = unix.IPPROTO_UDP 39 rule.UIDRange = NewRuleUIDRange(100, 100) 40 rule.Protocol = unix.RTPROT_KERNEL 41 if err := RuleAdd(rule); err != nil { 42 t.Fatal(err) 43 } 44 45 rules, err := RuleList(FAMILY_V4) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 if len(rules) != len(rulesBegin)+1 { 51 t.Fatal("Rule not added properly") 52 } 53 54 // find this rule 55 found := ruleExists(rules, *rule) 56 if !found { 57 t.Fatal("Rule has diffrent options than one added") 58 } 59 60 if err := RuleDel(rule); err != nil { 61 t.Fatal(err) 62 } 63 64 rulesEnd, err := RuleList(FAMILY_V4) 65 if err != nil { 66 t.Fatal(err) 67 } 68 69 if len(rulesEnd) != len(rulesBegin) { 70 t.Fatal("Rule not removed properly") 71 } 72 } 73 74 func TestRuleListFiltered(t *testing.T) { 75 skipUnlessRoot(t) 76 defer setUpNetlinkTest(t)() 77 78 t.Run("IPv4", testRuleListFilteredIPv4) 79 t.Run("IPv6", testRuleListFilteredIPv6) 80 } 81 82 func testRuleListFilteredIPv4(t *testing.T) { 83 srcNet := &net.IPNet{IP: net.IPv4(172, 16, 0, 1), Mask: net.CIDRMask(16, 32)} 84 dstNet := &net.IPNet{IP: net.IPv4(172, 16, 1, 1), Mask: net.CIDRMask(24, 32)} 85 runRuleListFiltered(t, FAMILY_V4, srcNet, dstNet) 86 } 87 88 func testRuleListFilteredIPv6(t *testing.T) { 89 ip1 := net.ParseIP("fd56:6b58:db28:2913::") 90 ip2 := net.ParseIP("fde9:379f:3b35:6635::") 91 92 srcNet := &net.IPNet{IP: ip1, Mask: net.CIDRMask(64, 128)} 93 dstNet := &net.IPNet{IP: ip2, Mask: net.CIDRMask(96, 128)} 94 runRuleListFiltered(t, FAMILY_V6, srcNet, dstNet) 95 } 96 97 func runRuleListFiltered(t *testing.T, family int, srcNet, dstNet *net.IPNet) { 98 defaultRules, _ := RuleList(family) 99 100 tests := []struct { 101 name string 102 ruleFilter *Rule 103 filterMask uint64 104 preRun func() *Rule // Creates sample rule harness 105 postRun func(*Rule) // Deletes sample rule harness 106 setupWant func(*Rule) ([]Rule, bool) 107 }{ 108 { 109 name: "returns all rules", 110 ruleFilter: nil, 111 filterMask: 0, 112 preRun: func() *Rule { return nil }, 113 postRun: func(r *Rule) {}, 114 setupWant: func(_ *Rule) ([]Rule, bool) { 115 return defaultRules, false 116 }, 117 }, 118 { 119 name: "returns one rule filtered by Src", 120 ruleFilter: &Rule{Src: srcNet}, 121 filterMask: RT_FILTER_SRC, 122 preRun: func() *Rule { 123 r := NewRule() 124 r.Src = srcNet 125 r.Priority = 1 // Must add priority and table otherwise it's auto-assigned 126 r.Family = family 127 r.Table = 1 128 RuleAdd(r) 129 return r 130 }, 131 postRun: func(r *Rule) { RuleDel(r) }, 132 setupWant: func(r *Rule) ([]Rule, bool) { 133 return []Rule{*r}, false 134 }, 135 }, 136 { 137 name: "returns one rule filtered by Dst", 138 ruleFilter: &Rule{Dst: dstNet}, 139 filterMask: RT_FILTER_DST, 140 preRun: func() *Rule { 141 r := NewRule() 142 r.Dst = dstNet 143 r.Priority = 1 // Must add priority and table otherwise it's auto-assigned 144 r.Family = family 145 r.Table = 1 146 RuleAdd(r) 147 return r 148 }, 149 postRun: func(r *Rule) { RuleDel(r) }, 150 setupWant: func(r *Rule) ([]Rule, bool) { 151 return []Rule{*r}, false 152 }, 153 }, 154 { 155 name: "returns two rules filtered by Dst", 156 ruleFilter: &Rule{Dst: dstNet}, 157 filterMask: RT_FILTER_DST, 158 preRun: func() *Rule { 159 r := NewRule() 160 r.Dst = dstNet 161 r.Priority = 1 // Must add priority and table otherwise it's auto-assigned 162 r.Family = family 163 r.Table = 1 164 RuleAdd(r) 165 166 rc := *r // Create almost identical copy 167 rc.Src = srcNet 168 RuleAdd(&rc) 169 170 return r 171 }, 172 postRun: func(r *Rule) { 173 RuleDel(r) 174 175 rc := *r // Delete the almost identical copy 176 rc.Src = srcNet 177 RuleDel(&rc) 178 }, 179 setupWant: func(r *Rule) ([]Rule, bool) { 180 rs := []Rule{} 181 rs = append(rs, *r) 182 183 rc := *r // Append the almost identical copy 184 rc.Src = srcNet 185 rs = append(rs, rc) 186 187 return rs, false 188 }, 189 }, 190 { 191 name: "returns one rule filtered by Src when two rules exist", 192 ruleFilter: &Rule{Src: srcNet}, 193 filterMask: RT_FILTER_SRC, 194 preRun: func() *Rule { 195 r := NewRule() 196 r.Dst = dstNet 197 r.Priority = 1 // Must add priority and table otherwise it's auto-assigned 198 r.Family = family 199 r.Table = 1 200 RuleAdd(r) 201 202 rc := *r // Create almost identical copy 203 rc.Src = srcNet 204 RuleAdd(&rc) 205 206 return r 207 }, 208 postRun: func(r *Rule) { 209 RuleDel(r) 210 211 rc := *r // Delete the almost identical copy 212 rc.Src = srcNet 213 RuleDel(&rc) 214 }, 215 setupWant: func(r *Rule) ([]Rule, bool) { 216 rs := []Rule{} 217 // Do not append `r` 218 219 rc := *r // Append the almost identical copy 220 rc.Src = srcNet 221 rs = append(rs, rc) 222 223 return rs, false 224 }, 225 }, 226 { 227 name: "returns one rule filtered by Priority(0) and Table", 228 ruleFilter: &Rule{Priority: 0, Table: 1}, 229 filterMask: RT_FILTER_PRIORITY | RT_FILTER_TABLE, 230 preRun: func() *Rule { 231 r := NewRule() 232 r.Src = srcNet 233 r.Priority = 0 234 r.Family = family 235 r.Table = 1 236 RuleAdd(r) 237 return r 238 }, 239 postRun: func(r *Rule) { 240 RuleDel(r) 241 }, 242 setupWant: func(r *Rule) ([]Rule, bool) { 243 return []Rule{*r}, false 244 }, 245 }, 246 { 247 name: "returns one rule filtered by Priority preceding main-table rule", 248 ruleFilter: &Rule{Priority: 32765}, 249 filterMask: RT_FILTER_PRIORITY, 250 preRun: func() *Rule { 251 r := NewRule() 252 r.Src = srcNet 253 r.Family = family 254 r.Table = 1 255 RuleAdd(r) 256 257 r.Priority = 32765 // Set priority for assertion 258 return r 259 }, 260 postRun: func(r *Rule) { 261 RuleDel(r) 262 }, 263 setupWant: func(r *Rule) ([]Rule, bool) { 264 return []Rule{*r}, false 265 }, 266 }, 267 { 268 name: "returns rules with specific priority", 269 ruleFilter: &Rule{Priority: 5}, 270 filterMask: RT_FILTER_PRIORITY, 271 preRun: func() *Rule { 272 r := NewRule() 273 r.Src = srcNet 274 r.Priority = 5 275 r.Family = family 276 r.Table = 1 277 RuleAdd(r) 278 279 for i := 2; i < 5; i++ { 280 rc := *r // Create almost identical copy 281 rc.Table = i 282 RuleAdd(&rc) 283 } 284 285 return r 286 }, 287 postRun: func(r *Rule) { 288 RuleDel(r) 289 290 for i := 2; i < 5; i++ { 291 rc := *r // Delete the almost identical copy 292 rc.Table = -1 293 RuleDel(&rc) 294 } 295 }, 296 setupWant: func(r *Rule) ([]Rule, bool) { 297 rs := []Rule{} 298 rs = append(rs, *r) 299 300 for i := 2; i < 5; i++ { 301 rc := *r // Append the almost identical copy 302 rc.Table = i 303 rs = append(rs, rc) 304 } 305 306 return rs, false 307 }, 308 }, 309 { 310 name: "returns rules filtered by Table", 311 ruleFilter: &Rule{Table: 199}, 312 filterMask: RT_FILTER_TABLE, 313 preRun: func() *Rule { 314 r := NewRule() 315 r.Src = srcNet 316 r.Priority = 1 // Must add priority otherwise it's auto-assigned 317 r.Family = family 318 r.Table = 199 319 RuleAdd(r) 320 return r 321 }, 322 postRun: func(r *Rule) { RuleDel(r) }, 323 setupWant: func(r *Rule) ([]Rule, bool) { 324 return []Rule{*r}, false 325 }, 326 }, 327 { 328 name: "returns rules filtered by Mask", 329 ruleFilter: &Rule{Mask: &[]uint32{0x5}[0]}, 330 filterMask: RT_FILTER_MASK, 331 preRun: func() *Rule { 332 r := NewRule() 333 r.Src = srcNet 334 r.Priority = 1 // Must add priority and table otherwise it's auto-assigned 335 r.Family = family 336 r.Table = 1 337 r.Mask = &[]uint32{0x5}[0] 338 RuleAdd(r) 339 return r 340 }, 341 postRun: func(r *Rule) { RuleDel(r) }, 342 setupWant: func(r *Rule) ([]Rule, bool) { 343 return []Rule{*r}, false 344 }, 345 }, 346 { 347 name: "returns rules filtered by Mark", 348 ruleFilter: &Rule{Mark: 0xbb}, 349 filterMask: RT_FILTER_MARK, 350 preRun: func() *Rule { 351 r := NewRule() 352 r.Src = srcNet 353 r.Priority = 1 // Must add priority, table, mask otherwise it's auto-assigned 354 r.Family = family 355 r.Table = 1 356 r.Mask = &[]uint32{0xff}[0] 357 r.Mark = 0xbb 358 RuleAdd(r) 359 return r 360 }, 361 postRun: func(r *Rule) { RuleDel(r) }, 362 setupWant: func(r *Rule) ([]Rule, bool) { 363 return []Rule{*r}, false 364 }, 365 }, 366 { 367 name: "returns rules filtered by fwmark 0", 368 ruleFilter: &Rule{Mark: 0, Mask: nil, Table: 100}, 369 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 370 preRun: func() *Rule { 371 r := NewRule() 372 r.Src = srcNet 373 r.Priority = 1 374 r.Family = family 375 r.Table = 100 376 r.Mark = 0 377 r.Mask = nil 378 if err := RuleAdd(r); err != nil { 379 t.Fatal(err) 380 } 381 return r 382 }, 383 postRun: func(r *Rule) { RuleDel(r) }, 384 setupWant: func(r *Rule) ([]Rule, bool) { 385 return []Rule{*r}, false 386 }, 387 }, 388 { 389 name: "returns rules filtered by fwmark 0/0xFFFFFFFF", 390 ruleFilter: &Rule{Mark: 0, Mask: &[]uint32{0xFFFFFFFF}[0], Table: 100}, 391 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 392 preRun: func() *Rule { 393 r := NewRule() 394 r.Src = srcNet 395 r.Priority = 1 396 r.Family = family 397 r.Table = 100 398 r.Mark = 0 399 r.Mask = &[]uint32{0xFFFFFFFF}[0] 400 if err := RuleAdd(r); err != nil { 401 t.Fatal(err) 402 } 403 return r 404 }, 405 postRun: func(r *Rule) { RuleDel(r) }, 406 setupWant: func(r *Rule) ([]Rule, bool) { 407 return []Rule{*r}, false 408 }, 409 }, 410 { 411 name: "returns rules filtered by fwmark 0x1234/0", 412 ruleFilter: &Rule{Mark: 0x1234, Mask: &[]uint32{0}[0], Table: 100}, 413 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 414 preRun: func() *Rule { 415 r := NewRule() 416 r.Src = srcNet 417 r.Priority = 1 418 r.Family = family 419 r.Table = 100 420 r.Mark = 0x1234 421 r.Mask = &[]uint32{0}[0] 422 if err := RuleAdd(r); err != nil { 423 t.Fatal(err) 424 } 425 return r 426 }, 427 postRun: func(r *Rule) { RuleDel(r) }, 428 setupWant: func(r *Rule) ([]Rule, bool) { 429 return []Rule{*r}, false 430 }, 431 }, 432 { 433 name: "returns rules filtered by fwmark 0/0xFFFFFFFF", 434 ruleFilter: &Rule{Mark: 0, Mask: &[]uint32{0xFFFFFFFF}[0], Table: 100}, 435 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 436 preRun: func() *Rule { 437 r := NewRule() 438 r.Src = srcNet 439 r.Priority = 1 440 r.Family = family 441 r.Table = 100 442 r.Mark = 0 443 r.Mask = &[]uint32{0xFFFFFFFF}[0] 444 if err := RuleAdd(r); err != nil { 445 t.Fatal(err) 446 } 447 return r 448 }, 449 postRun: func(r *Rule) { RuleDel(r) }, 450 setupWant: func(r *Rule) ([]Rule, bool) { 451 return []Rule{*r}, false 452 }, 453 }, 454 { 455 name: "returns rules filtered by fwmark 0xFFFFFFFF", 456 ruleFilter: &Rule{Mark: 0xFFFFFFFF, Mask: &[]uint32{0xFFFFFFFF}[0], Table: 100}, 457 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 458 preRun: func() *Rule { 459 r := NewRule() 460 r.Src = srcNet 461 r.Priority = 1 462 r.Family = family 463 r.Table = 100 464 r.Mark = 0xFFFFFFFF 465 r.Mask = nil 466 if err := RuleAdd(r); err != nil { 467 t.Fatal(err) 468 } 469 return r 470 }, 471 postRun: func(r *Rule) { RuleDel(r) }, 472 setupWant: func(r *Rule) ([]Rule, bool) { 473 return []Rule{*r}, false 474 }, 475 }, 476 { 477 name: "returns rules filtered by fwmark 0x1234", 478 ruleFilter: &Rule{Mark: 0x1234, Mask: &[]uint32{0xFFFFFFFF}[0], Table: 100}, 479 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 480 preRun: func() *Rule { 481 r := NewRule() 482 r.Src = srcNet 483 r.Priority = 1 484 r.Family = family 485 r.Table = 100 486 r.Mark = 0x1234 487 r.Mask = nil 488 if err := RuleAdd(r); err != nil { 489 t.Fatal(err) 490 } 491 return r 492 }, 493 postRun: func(r *Rule) { RuleDel(r) }, 494 setupWant: func(r *Rule) ([]Rule, bool) { 495 return []Rule{*r}, false 496 }, 497 }, 498 { 499 name: "returns rules filtered by fwmark 0x12345678", 500 ruleFilter: &Rule{Mark: 0x12345678, Mask: &[]uint32{0xFFFFFFFF}[0], Table: 100}, 501 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 502 preRun: func() *Rule { 503 r := NewRule() 504 r.Src = srcNet 505 r.Priority = 1 506 r.Family = family 507 r.Table = 100 508 r.Mark = 0x12345678 509 r.Mask = nil 510 if err := RuleAdd(r); err != nil { 511 t.Fatal(err) 512 } 513 return r 514 }, 515 postRun: func(r *Rule) { RuleDel(r) }, 516 setupWant: func(r *Rule) ([]Rule, bool) { 517 return []Rule{*r}, false 518 }, 519 }, 520 { 521 name: "returns rules filtered by fwmark 0xFFFFFFFF/0", 522 ruleFilter: &Rule{Mark: 0xFFFFFFFF, Mask: &[]uint32{0}[0], Table: 100}, 523 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 524 preRun: func() *Rule { 525 r := NewRule() 526 r.Src = srcNet 527 r.Priority = 1 528 r.Family = family 529 r.Table = 100 530 r.Mark = 0xFFFFFFFF 531 r.Mask = &[]uint32{0}[0] 532 if err := RuleAdd(r); err != nil { 533 t.Fatal(err) 534 } 535 return r 536 }, 537 postRun: func(r *Rule) { RuleDel(r) }, 538 setupWant: func(r *Rule) ([]Rule, bool) { 539 return []Rule{*r}, false 540 }, 541 }, 542 { 543 name: "returns rules filtered by fwmark 0xFFFFFFFF/0xFFFFFFFF", 544 ruleFilter: &Rule{Mark: 0xFFFFFFFF, Mask: &[]uint32{0xFFFFFFFF}[0], Table: 100}, 545 filterMask: RT_FILTER_MARK | RT_FILTER_MASK | RT_FILTER_TABLE, 546 preRun: func() *Rule { 547 r := NewRule() 548 r.Src = srcNet 549 r.Priority = 1 550 r.Family = family 551 r.Table = 100 552 r.Mark = 0xFFFFFFFF 553 r.Mask = &[]uint32{0xFFFFFFFF}[0] 554 if err := RuleAdd(r); err != nil { 555 t.Fatal(err) 556 } 557 return r 558 }, 559 postRun: func(r *Rule) { RuleDel(r) }, 560 setupWant: func(r *Rule) ([]Rule, bool) { 561 return []Rule{*r}, false 562 }, 563 }, 564 { 565 name: "returns rules filtered by Tos", 566 ruleFilter: &Rule{Tos: 12}, 567 filterMask: RT_FILTER_TOS, 568 preRun: func() *Rule { 569 r := NewRule() 570 r.Src = srcNet 571 r.Priority = 1 // Must add priority, table, mask otherwise it's auto-assigned 572 r.Family = family 573 r.Table = 12 574 r.Tos = 12 // Tos must equal table 575 RuleAdd(r) 576 return r 577 }, 578 postRun: func(r *Rule) { RuleDel(r) }, 579 setupWant: func(r *Rule) ([]Rule, bool) { 580 return []Rule{*r}, false 581 }, 582 }, 583 } 584 for _, tt := range tests { 585 t.Run(tt.name, func(t *testing.T) { 586 rule := tt.preRun() 587 wantRules, wantErr := tt.setupWant(rule) 588 589 rules, err := RuleListFiltered(family, tt.ruleFilter, tt.filterMask) 590 for i := 0; i < len(wantRules); i++ { 591 if len(wantRules) == len(rules) { 592 break 593 } 594 time.Sleep(1 * time.Second) // wait rule take effect 595 rules, err = RuleListFiltered(family, tt.ruleFilter, tt.filterMask) 596 } 597 tt.postRun(rule) 598 599 if len(wantRules) != len(rules) { 600 t.Errorf("Expected len: %d, got: %d", len(wantRules), len(rules)) 601 } else { 602 for i := range wantRules { 603 if !ruleEquals(wantRules[i], rules[i]) { 604 t.Errorf("Rules mismatch, want %v, got %v", wantRules[i], rules[i]) 605 } 606 } 607 } 608 609 if (err != nil) != wantErr { 610 t.Errorf("Error expectation not met, want %v, got %v", (err != nil), wantErr) 611 } 612 }) 613 } 614 } 615 616 func TestRuleString(t *testing.T) { 617 t.Parallel() 618 619 testCases := map[string]struct { 620 r Rule 621 s string 622 }{ 623 "empty rule": { 624 s: "ip rule 0: from all to all table 0 ", 625 }, 626 "rule with src and dst equivalent to <nil>": { 627 r: Rule{ 628 Priority: 100, 629 Src: &net.IPNet{IP: net.IPv4(10, 0, 0, 0)}, 630 Dst: &net.IPNet{IP: net.IPv4(20, 0, 0, 0)}, 631 Table: 99, 632 }, 633 s: "ip rule 100: from all to all table 99 ", 634 }, 635 "rule with src and dst": { 636 r: Rule{ 637 Priority: 100, 638 Src: &net.IPNet{IP: net.IPv4(10, 0, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, 639 Dst: &net.IPNet{IP: net.IPv4(20, 0, 0, 0), Mask: net.IPv4Mask(255, 255, 255, 0)}, 640 Table: 99, 641 }, 642 s: "ip rule 100: from 10.0.0.0/24 to 20.0.0.0/24 table 99 ", 643 }, 644 "rule with type": { 645 r: Rule{ 646 Priority: 101, 647 Type: unix.RTN_UNREACHABLE, 648 }, 649 s: "ip rule 101: from all to all table 0 unreachable", 650 }, 651 } 652 653 for name, testCase := range testCases { 654 testCase := testCase 655 t.Run(name, func(t *testing.T) { 656 t.Parallel() 657 658 s := testCase.r.String() 659 660 if s != testCase.s { 661 t.Errorf("expected %q but got %q", testCase.s, s) 662 } 663 }) 664 } 665 } 666 667 func ruleExists(rules []Rule, rule Rule) bool { 668 for i := range rules { 669 if ruleEquals(rules[i], rule) { 670 return true 671 } 672 } 673 674 return false 675 } 676 677 func ruleEquals(a, b Rule) bool { 678 return a.Table == b.Table && 679 ((a.Src == nil && b.Src == nil) || 680 (a.Src != nil && b.Src != nil && a.Src.String() == b.Src.String())) && 681 ((a.Dst == nil && b.Dst == nil) || 682 (a.Dst != nil && b.Dst != nil && a.Dst.String() == b.Dst.String())) && 683 a.OifName == b.OifName && 684 a.Priority == b.Priority && 685 a.Family == b.Family && 686 a.IifName == b.IifName && 687 a.Invert == b.Invert && 688 a.Tos == b.Tos && 689 a.Type == b.Type && 690 a.IPProto == b.IPProto && 691 a.Protocol == b.Protocol && 692 a.Mark == b.Mark && 693 (ptrEqual(a.Mask, b.Mask) || (a.Mark != 0 && 694 (a.Mask == nil && *b.Mask == 0xFFFFFFFF || b.Mask == nil && *a.Mask == 0xFFFFFFFF))) 695 }