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