gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/stack/nud_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package stack_test 16 17 import ( 18 "math" 19 "math/rand" 20 "testing" 21 "time" 22 23 "github.com/google/go-cmp/cmp" 24 "gvisor.dev/gvisor/pkg/tcpip" 25 "gvisor.dev/gvisor/pkg/tcpip/faketime" 26 "gvisor.dev/gvisor/pkg/tcpip/link/channel" 27 "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" 28 "gvisor.dev/gvisor/pkg/tcpip/stack" 29 ) 30 31 const ( 32 defaultBaseReachableTime = 30 * time.Second 33 minimumBaseReachableTime = time.Millisecond 34 defaultMinRandomFactor = 0.5 35 defaultMaxRandomFactor = 1.5 36 defaultRetransmitTimer = time.Second 37 minimumRetransmitTimer = time.Millisecond 38 defaultDelayFirstProbeTime = 5 * time.Second 39 defaultMaxMulticastProbes = 3 40 defaultMaxUnicastProbes = 3 41 42 defaultFakeRandomNum = 0.5 43 ) 44 45 // fakeRand is a deterministic random number generator. 46 type fakeRand struct { 47 num float32 48 } 49 50 var _ rand.Source = (*fakeRand)(nil) 51 52 func (f *fakeRand) Int63() int64 { 53 return int64(f.num * float32(1<<63)) 54 } 55 56 func (*fakeRand) Seed(int64) {} 57 58 func TestNUDFunctions(t *testing.T) { 59 const nicID = 1 60 61 tests := []struct { 62 name string 63 nicID tcpip.NICID 64 netProtoFactory []stack.NetworkProtocolFactory 65 extraLinkCapabilities stack.LinkEndpointCapabilities 66 expectedErr tcpip.Error 67 }{ 68 { 69 name: "Invalid NICID", 70 nicID: nicID + 1, 71 netProtoFactory: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 72 extraLinkCapabilities: stack.CapabilityResolutionRequired, 73 expectedErr: &tcpip.ErrUnknownNICID{}, 74 }, 75 { 76 name: "No network protocol", 77 nicID: nicID, 78 expectedErr: &tcpip.ErrNotSupported{}, 79 }, 80 { 81 name: "With IPv6", 82 nicID: nicID, 83 netProtoFactory: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 84 expectedErr: &tcpip.ErrNotSupported{}, 85 }, 86 { 87 name: "With resolution capability", 88 nicID: nicID, 89 extraLinkCapabilities: stack.CapabilityResolutionRequired, 90 expectedErr: &tcpip.ErrNotSupported{}, 91 }, 92 { 93 name: "With IPv6 and resolution capability", 94 nicID: nicID, 95 netProtoFactory: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 96 extraLinkCapabilities: stack.CapabilityResolutionRequired, 97 }, 98 } 99 100 for _, test := range tests { 101 t.Run(test.name, func(t *testing.T) { 102 clock := faketime.NewManualClock() 103 s := stack.New(stack.Options{ 104 NUDConfigs: stack.DefaultNUDConfigurations(), 105 NetworkProtocols: test.netProtoFactory, 106 Clock: clock, 107 }) 108 109 e := channel.New(0, 0, linkAddr1) 110 e.LinkEPCapabilities &^= stack.CapabilityResolutionRequired 111 e.LinkEPCapabilities |= test.extraLinkCapabilities 112 113 if err := s.CreateNIC(nicID, e); err != nil { 114 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 115 } 116 117 configs := stack.DefaultNUDConfigurations() 118 configs.BaseReachableTime = time.Hour 119 120 { 121 err := s.SetNUDConfigurations(test.nicID, ipv6.ProtocolNumber, configs) 122 if diff := cmp.Diff(test.expectedErr, err); diff != "" { 123 t.Errorf("s.SetNUDConfigurations(%d, %d, _) error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, diff) 124 } 125 } 126 127 { 128 gotConfigs, err := s.NUDConfigurations(test.nicID, ipv6.ProtocolNumber) 129 if diff := cmp.Diff(test.expectedErr, err); diff != "" { 130 t.Errorf("s.NUDConfigurations(%d, %d) error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, diff) 131 } else if test.expectedErr == nil { 132 if diff := cmp.Diff(configs, gotConfigs); diff != "" { 133 t.Errorf("got configs mismatch (-want +got):\n%s", diff) 134 } 135 } 136 } 137 138 for _, addr := range []tcpip.Address{llAddr1, llAddr2} { 139 { 140 err := s.AddStaticNeighbor(test.nicID, ipv6.ProtocolNumber, addr, linkAddr1) 141 if diff := cmp.Diff(test.expectedErr, err); diff != "" { 142 t.Errorf("s.AddStaticNeighbor(%d, %d, %s, %s) error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, addr, linkAddr1, diff) 143 } 144 } 145 } 146 147 { 148 wantErr := test.expectedErr 149 for i := 0; i < 2; i++ { 150 { 151 err := s.RemoveNeighbor(test.nicID, ipv6.ProtocolNumber, llAddr1) 152 if diff := cmp.Diff(wantErr, err); diff != "" { 153 t.Errorf("s.RemoveNeighbor(%d, %d, '') error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, diff) 154 } 155 } 156 157 if test.expectedErr != nil { 158 break 159 } 160 161 // Removing a neighbor that does not exist should give us a bad address 162 // error. 163 wantErr = &tcpip.ErrBadAddress{} 164 } 165 } 166 167 { 168 neighbors, err := s.Neighbors(test.nicID, ipv6.ProtocolNumber) 169 if diff := cmp.Diff(test.expectedErr, err); diff != "" { 170 t.Errorf("s.Neighbors(%d, %d) error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, diff) 171 } else if test.expectedErr == nil { 172 if diff := cmp.Diff( 173 []stack.NeighborEntry{{Addr: llAddr2, LinkAddr: linkAddr1, State: stack.Static, UpdatedAt: clock.NowMonotonic()}}, 174 neighbors, 175 cmp.AllowUnexported(tcpip.MonotonicTime{}), 176 ); diff != "" { 177 t.Errorf("neighbors mismatch (-want +got):\n%s", diff) 178 } 179 } 180 } 181 182 { 183 err := s.ClearNeighbors(test.nicID, ipv6.ProtocolNumber) 184 if diff := cmp.Diff(test.expectedErr, err); diff != "" { 185 t.Errorf("s.ClearNeigbors(%d, %d) error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, diff) 186 } else if test.expectedErr == nil { 187 if neighbors, err := s.Neighbors(test.nicID, ipv6.ProtocolNumber); err != nil { 188 t.Errorf("s.Neighbors(%d, %d): %s", test.nicID, ipv6.ProtocolNumber, err) 189 } else if len(neighbors) != 0 { 190 t.Errorf("got len(neighbors) = %d, want = 0; neighbors = %#v", len(neighbors), neighbors) 191 } 192 } 193 } 194 }) 195 } 196 } 197 198 func TestDefaultNUDConfigurations(t *testing.T) { 199 const nicID = 1 200 201 e := channel.New(0, 1280, linkAddr1) 202 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 203 204 s := stack.New(stack.Options{ 205 // A neighbor cache is required to store NUDConfigurations. The networking 206 // stack will only allocate neighbor caches if a protocol providing link 207 // address resolution is specified (e.g. ARP or IPv6). 208 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 209 NUDConfigs: stack.DefaultNUDConfigurations(), 210 }) 211 if err := s.CreateNIC(nicID, e); err != nil { 212 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 213 } 214 c, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 215 if err != nil { 216 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 217 } 218 if got, want := c, stack.DefaultNUDConfigurations(); got != want { 219 t.Errorf("got stack.NUDConfigurations(%d, %d) = %+v, want = %+v", nicID, ipv6.ProtocolNumber, got, want) 220 } 221 } 222 223 func TestNUDConfigurationsBaseReachableTime(t *testing.T) { 224 tests := []struct { 225 name string 226 baseReachableTime time.Duration 227 want time.Duration 228 }{ 229 // Invalid cases 230 { 231 name: "EqualToZero", 232 baseReachableTime: 0, 233 want: defaultBaseReachableTime, 234 }, 235 // Valid cases 236 { 237 name: "MoreThanZero", 238 baseReachableTime: time.Millisecond, 239 want: time.Millisecond, 240 }, 241 { 242 name: "MoreThanDefaultBaseReachableTime", 243 baseReachableTime: 2 * defaultBaseReachableTime, 244 want: 2 * defaultBaseReachableTime, 245 }, 246 } 247 248 for _, test := range tests { 249 t.Run(test.name, func(t *testing.T) { 250 const nicID = 1 251 252 c := stack.DefaultNUDConfigurations() 253 c.BaseReachableTime = test.baseReachableTime 254 255 e := channel.New(0, 1280, linkAddr1) 256 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 257 258 s := stack.New(stack.Options{ 259 // A neighbor cache is required to store NUDConfigurations. The 260 // networking stack will only allocate neighbor caches if a protocol 261 // providing link address resolution is specified (e.g. ARP or IPv6). 262 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 263 NUDConfigs: c, 264 }) 265 if err := s.CreateNIC(nicID, e); err != nil { 266 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 267 } 268 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 269 if err != nil { 270 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 271 } 272 if got := sc.BaseReachableTime; got != test.want { 273 t.Errorf("got BaseReachableTime = %q, want = %q", got, test.want) 274 } 275 }) 276 } 277 } 278 279 func TestNUDConfigurationsMinRandomFactor(t *testing.T) { 280 tests := []struct { 281 name string 282 minRandomFactor float32 283 want float32 284 }{ 285 // Invalid cases 286 { 287 name: "LessThanZero", 288 minRandomFactor: -1, 289 want: defaultMinRandomFactor, 290 }, 291 { 292 name: "EqualToZero", 293 minRandomFactor: 0, 294 want: defaultMinRandomFactor, 295 }, 296 // Valid cases 297 { 298 name: "MoreThanZero", 299 minRandomFactor: 1, 300 want: 1, 301 }, 302 } 303 304 for _, test := range tests { 305 t.Run(test.name, func(t *testing.T) { 306 const nicID = 1 307 308 c := stack.DefaultNUDConfigurations() 309 c.MinRandomFactor = test.minRandomFactor 310 311 e := channel.New(0, 1280, linkAddr1) 312 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 313 314 s := stack.New(stack.Options{ 315 // A neighbor cache is required to store NUDConfigurations. The 316 // networking stack will only allocate neighbor caches if a protocol 317 // providing link address resolution is specified (e.g. ARP or IPv6). 318 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 319 NUDConfigs: c, 320 }) 321 if err := s.CreateNIC(nicID, e); err != nil { 322 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 323 } 324 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 325 if err != nil { 326 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 327 } 328 if got := sc.MinRandomFactor; got != test.want { 329 t.Errorf("got MinRandomFactor = %f, want = %f", got, test.want) 330 } 331 }) 332 } 333 } 334 335 func TestNUDConfigurationsMaxRandomFactor(t *testing.T) { 336 tests := []struct { 337 name string 338 minRandomFactor float32 339 maxRandomFactor float32 340 want float32 341 }{ 342 // Invalid cases 343 { 344 name: "LessThanZero", 345 minRandomFactor: defaultMinRandomFactor, 346 maxRandomFactor: -1, 347 want: defaultMaxRandomFactor, 348 }, 349 { 350 name: "EqualToZero", 351 minRandomFactor: defaultMinRandomFactor, 352 maxRandomFactor: 0, 353 want: defaultMaxRandomFactor, 354 }, 355 { 356 name: "LessThanMinRandomFactor", 357 minRandomFactor: defaultMinRandomFactor, 358 maxRandomFactor: defaultMinRandomFactor * 0.99, 359 want: defaultMaxRandomFactor, 360 }, 361 { 362 name: "MoreThanMinRandomFactorWhenMinRandomFactorIsLargerThanMaxRandomFactorDefault", 363 minRandomFactor: defaultMaxRandomFactor * 2, 364 maxRandomFactor: defaultMaxRandomFactor, 365 want: defaultMaxRandomFactor * 6, 366 }, 367 // Valid cases 368 { 369 name: "EqualToMinRandomFactor", 370 minRandomFactor: defaultMinRandomFactor, 371 maxRandomFactor: defaultMinRandomFactor, 372 want: defaultMinRandomFactor, 373 }, 374 { 375 name: "MoreThanMinRandomFactor", 376 minRandomFactor: defaultMinRandomFactor, 377 maxRandomFactor: defaultMinRandomFactor * 1.1, 378 want: defaultMinRandomFactor * 1.1, 379 }, 380 } 381 382 for _, test := range tests { 383 t.Run(test.name, func(t *testing.T) { 384 const nicID = 1 385 386 c := stack.DefaultNUDConfigurations() 387 c.MinRandomFactor = test.minRandomFactor 388 c.MaxRandomFactor = test.maxRandomFactor 389 390 e := channel.New(0, 1280, linkAddr1) 391 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 392 393 s := stack.New(stack.Options{ 394 // A neighbor cache is required to store NUDConfigurations. The 395 // networking stack will only allocate neighbor caches if a protocol 396 // providing link address resolution is specified (e.g. ARP or IPv6). 397 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 398 NUDConfigs: c, 399 }) 400 if err := s.CreateNIC(nicID, e); err != nil { 401 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 402 } 403 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 404 if err != nil { 405 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 406 } 407 if got := sc.MaxRandomFactor; got != test.want { 408 t.Errorf("got MaxRandomFactor = %f, want = %f", got, test.want) 409 } 410 }) 411 } 412 } 413 414 func TestNUDConfigurationsRetransmitTimer(t *testing.T) { 415 tests := []struct { 416 name string 417 retransmitTimer time.Duration 418 want time.Duration 419 }{ 420 // Invalid cases 421 { 422 name: "EqualToZero", 423 retransmitTimer: 0, 424 want: defaultRetransmitTimer, 425 }, 426 { 427 name: "LessThanMinimumRetransmitTimer", 428 retransmitTimer: minimumRetransmitTimer - time.Nanosecond, 429 want: defaultRetransmitTimer, 430 }, 431 // Valid cases 432 { 433 name: "EqualToMinimumRetransmitTimer", 434 retransmitTimer: minimumRetransmitTimer, 435 want: minimumBaseReachableTime, 436 }, 437 { 438 name: "LargetThanMinimumRetransmitTimer", 439 retransmitTimer: 2 * minimumBaseReachableTime, 440 want: 2 * minimumBaseReachableTime, 441 }, 442 } 443 444 for _, test := range tests { 445 t.Run(test.name, func(t *testing.T) { 446 const nicID = 1 447 448 c := stack.DefaultNUDConfigurations() 449 c.RetransmitTimer = test.retransmitTimer 450 451 e := channel.New(0, 1280, linkAddr1) 452 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 453 454 s := stack.New(stack.Options{ 455 // A neighbor cache is required to store NUDConfigurations. The 456 // networking stack will only allocate neighbor caches if a protocol 457 // providing link address resolution is specified (e.g. ARP or IPv6). 458 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 459 NUDConfigs: c, 460 }) 461 if err := s.CreateNIC(nicID, e); err != nil { 462 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 463 } 464 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 465 if err != nil { 466 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 467 } 468 if got := sc.RetransmitTimer; got != test.want { 469 t.Errorf("got RetransmitTimer = %q, want = %q", got, test.want) 470 } 471 }) 472 } 473 } 474 475 func TestNUDConfigurationsDelayFirstProbeTime(t *testing.T) { 476 tests := []struct { 477 name string 478 delayFirstProbeTime time.Duration 479 want time.Duration 480 }{ 481 // Invalid cases 482 { 483 name: "EqualToZero", 484 delayFirstProbeTime: 0, 485 want: defaultDelayFirstProbeTime, 486 }, 487 // Valid cases 488 { 489 name: "MoreThanZero", 490 delayFirstProbeTime: time.Millisecond, 491 want: time.Millisecond, 492 }, 493 } 494 495 for _, test := range tests { 496 t.Run(test.name, func(t *testing.T) { 497 const nicID = 1 498 499 c := stack.DefaultNUDConfigurations() 500 c.DelayFirstProbeTime = test.delayFirstProbeTime 501 502 e := channel.New(0, 1280, linkAddr1) 503 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 504 505 s := stack.New(stack.Options{ 506 // A neighbor cache is required to store NUDConfigurations. The 507 // networking stack will only allocate neighbor caches if a protocol 508 // providing link address resolution is specified (e.g. ARP or IPv6). 509 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 510 NUDConfigs: c, 511 }) 512 if err := s.CreateNIC(nicID, e); err != nil { 513 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 514 } 515 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 516 if err != nil { 517 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 518 } 519 if got := sc.DelayFirstProbeTime; got != test.want { 520 t.Errorf("got DelayFirstProbeTime = %q, want = %q", got, test.want) 521 } 522 }) 523 } 524 } 525 526 func TestNUDConfigurationsMaxMulticastProbes(t *testing.T) { 527 tests := []struct { 528 name string 529 maxMulticastProbes uint32 530 want uint32 531 }{ 532 // Invalid cases 533 { 534 name: "EqualToZero", 535 maxMulticastProbes: 0, 536 want: defaultMaxMulticastProbes, 537 }, 538 // Valid cases 539 { 540 name: "MoreThanZero", 541 maxMulticastProbes: 1, 542 want: 1, 543 }, 544 } 545 546 for _, test := range tests { 547 t.Run(test.name, func(t *testing.T) { 548 const nicID = 1 549 550 c := stack.DefaultNUDConfigurations() 551 c.MaxMulticastProbes = test.maxMulticastProbes 552 553 e := channel.New(0, 1280, linkAddr1) 554 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 555 556 s := stack.New(stack.Options{ 557 // A neighbor cache is required to store NUDConfigurations. The 558 // networking stack will only allocate neighbor caches if a protocol 559 // providing link address resolution is specified (e.g. ARP or IPv6). 560 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 561 NUDConfigs: c, 562 }) 563 if err := s.CreateNIC(nicID, e); err != nil { 564 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 565 } 566 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 567 if err != nil { 568 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 569 } 570 if got := sc.MaxMulticastProbes; got != test.want { 571 t.Errorf("got MaxMulticastProbes = %q, want = %q", got, test.want) 572 } 573 }) 574 } 575 } 576 577 func TestNUDConfigurationsMaxUnicastProbes(t *testing.T) { 578 tests := []struct { 579 name string 580 maxUnicastProbes uint32 581 want uint32 582 }{ 583 // Invalid cases 584 { 585 name: "EqualToZero", 586 maxUnicastProbes: 0, 587 want: defaultMaxUnicastProbes, 588 }, 589 // Valid cases 590 { 591 name: "MoreThanZero", 592 maxUnicastProbes: 1, 593 want: 1, 594 }, 595 } 596 597 for _, test := range tests { 598 t.Run(test.name, func(t *testing.T) { 599 const nicID = 1 600 601 c := stack.DefaultNUDConfigurations() 602 c.MaxUnicastProbes = test.maxUnicastProbes 603 604 e := channel.New(0, 1280, linkAddr1) 605 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 606 607 s := stack.New(stack.Options{ 608 // A neighbor cache is required to store NUDConfigurations. The 609 // networking stack will only allocate neighbor caches if a protocol 610 // providing link address resolution is specified (e.g. ARP or IPv6). 611 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 612 NUDConfigs: c, 613 }) 614 if err := s.CreateNIC(nicID, e); err != nil { 615 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 616 } 617 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 618 if err != nil { 619 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 620 } 621 if got := sc.MaxUnicastProbes; got != test.want { 622 t.Errorf("got MaxUnicastProbes = %q, want = %q", got, test.want) 623 } 624 }) 625 } 626 } 627 628 // TestNUDStateReachableTime verifies the correctness of the ReachableTime 629 // computation. 630 func TestNUDStateReachableTime(t *testing.T) { 631 tests := []struct { 632 name string 633 baseReachableTime time.Duration 634 minRandomFactor float32 635 maxRandomFactor float32 636 want time.Duration 637 }{ 638 { 639 name: "AllZeros", 640 baseReachableTime: 0, 641 minRandomFactor: 0, 642 maxRandomFactor: 0, 643 want: 0, 644 }, 645 { 646 name: "ZeroMaxRandomFactor", 647 baseReachableTime: time.Second, 648 minRandomFactor: 0, 649 maxRandomFactor: 0, 650 want: 0, 651 }, 652 { 653 name: "ZeroMinRandomFactor", 654 baseReachableTime: time.Second, 655 minRandomFactor: 0, 656 maxRandomFactor: 1, 657 want: time.Duration(defaultFakeRandomNum * float32(time.Second)), 658 }, 659 { 660 name: "FractionalRandomFactor", 661 baseReachableTime: time.Duration(math.MaxInt64), 662 minRandomFactor: 0.001, 663 maxRandomFactor: 0.002, 664 want: time.Duration((0.001 + (0.001 * defaultFakeRandomNum)) * float32(math.MaxInt64)), 665 }, 666 { 667 name: "MinAndMaxRandomFactorsEqual", 668 baseReachableTime: time.Second, 669 minRandomFactor: 1, 670 maxRandomFactor: 1, 671 want: time.Second, 672 }, 673 { 674 name: "MinAndMaxRandomFactorsDifferent", 675 baseReachableTime: time.Second, 676 minRandomFactor: 1, 677 maxRandomFactor: 2, 678 want: time.Duration((1.0 + defaultFakeRandomNum) * float32(time.Second)), 679 }, 680 { 681 name: "MaxInt64", 682 baseReachableTime: time.Duration(math.MaxInt64), 683 minRandomFactor: 1, 684 maxRandomFactor: 1, 685 want: time.Duration(math.MaxInt64), 686 }, 687 { 688 name: "Overflow", 689 baseReachableTime: time.Duration(math.MaxInt64), 690 minRandomFactor: 1.5, 691 maxRandomFactor: 1.5, 692 want: time.Duration(math.MaxInt64), 693 }, 694 { 695 name: "DoubleOverflow", 696 baseReachableTime: time.Duration(math.MaxInt64), 697 minRandomFactor: 2.5, 698 maxRandomFactor: 2.5, 699 want: time.Duration(math.MaxInt64), 700 }, 701 } 702 703 for _, test := range tests { 704 t.Run(test.name, func(t *testing.T) { 705 c := stack.NUDConfigurations{ 706 BaseReachableTime: test.baseReachableTime, 707 MinRandomFactor: test.minRandomFactor, 708 MaxRandomFactor: test.maxRandomFactor, 709 } 710 // A fake random number generator is used to ensure deterministic 711 // results. 712 rng := fakeRand{ 713 num: defaultFakeRandomNum, 714 } 715 var clock faketime.NullClock 716 s := stack.NewNUDState(c, &clock, rand.New(&rng)) 717 if got, want := s.ReachableTime(), test.want; got != want { 718 t.Errorf("got ReachableTime = %q, want = %q", got, want) 719 } 720 }) 721 } 722 } 723 724 // TestNUDStateRecomputeReachableTime exercises the ReachableTime function 725 // twice to verify recomputation of reachable time when the min random factor, 726 // max random factor, or base reachable time changes. 727 func TestNUDStateRecomputeReachableTime(t *testing.T) { 728 const defaultBase = time.Second 729 const defaultMin = 2.0 * defaultMaxRandomFactor 730 const defaultMax = 3.0 * defaultMaxRandomFactor 731 732 tests := []struct { 733 name string 734 baseReachableTime time.Duration 735 minRandomFactor float32 736 maxRandomFactor float32 737 want time.Duration 738 }{ 739 { 740 name: "BaseReachableTime", 741 baseReachableTime: 2 * defaultBase, 742 minRandomFactor: defaultMin, 743 maxRandomFactor: defaultMax, 744 want: time.Duration((defaultMin + (defaultMax-defaultMin)*defaultFakeRandomNum) * float32(2*defaultBase)), 745 }, 746 { 747 name: "MinRandomFactor", 748 baseReachableTime: defaultBase, 749 minRandomFactor: defaultMax, 750 maxRandomFactor: defaultMax, 751 want: time.Duration(defaultMax * float32(defaultBase)), 752 }, 753 { 754 name: "MaxRandomFactor", 755 baseReachableTime: defaultBase, 756 minRandomFactor: defaultMin, 757 maxRandomFactor: defaultMin, 758 want: time.Duration(defaultMin * float32(defaultBase)), 759 }, 760 { 761 name: "BothRandomFactor", 762 baseReachableTime: defaultBase, 763 minRandomFactor: 2 * defaultMin, 764 maxRandomFactor: 2 * defaultMax, 765 want: time.Duration((2*defaultMin + (2*defaultMax-2*defaultMin)*defaultFakeRandomNum) * float32(defaultBase)), 766 }, 767 { 768 name: "BaseReachableTimeAndBothRandomFactors", 769 baseReachableTime: 2 * defaultBase, 770 minRandomFactor: 2 * defaultMin, 771 maxRandomFactor: 2 * defaultMax, 772 want: time.Duration((2*defaultMin + (2*defaultMax-2*defaultMin)*defaultFakeRandomNum) * float32(2*defaultBase)), 773 }, 774 } 775 776 for _, test := range tests { 777 t.Run(test.name, func(t *testing.T) { 778 c := stack.DefaultNUDConfigurations() 779 c.BaseReachableTime = defaultBase 780 c.MinRandomFactor = defaultMin 781 c.MaxRandomFactor = defaultMax 782 783 // A fake random number generator is used to ensure deterministic 784 // results. 785 rng := fakeRand{ 786 num: defaultFakeRandomNum, 787 } 788 var clock faketime.NullClock 789 s := stack.NewNUDState(c, &clock, rand.New(&rng)) 790 old := s.ReachableTime() 791 792 if got, want := s.ReachableTime(), old; got != want { 793 t.Errorf("got ReachableTime = %q, want = %q", got, want) 794 } 795 796 // Check for recomputation when changing the min random factor, the max 797 // random factor, the base reachability time, or any permutation of those 798 // three options. 799 c.BaseReachableTime = test.baseReachableTime 800 c.MinRandomFactor = test.minRandomFactor 801 c.MaxRandomFactor = test.maxRandomFactor 802 s.SetConfig(c) 803 804 if got, want := s.ReachableTime(), test.want; got != want { 805 t.Errorf("got ReachableTime = %q, want = %q", got, want) 806 } 807 808 // Verify that ReachableTime isn't recomputed when none of the 809 // configuration options change. The random factor is changed so that if 810 // a recompution were to occur, ReachableTime would change. 811 rng.num = defaultFakeRandomNum / 2.0 812 if got, want := s.ReachableTime(), test.want; got != want { 813 t.Errorf("got ReachableTime = %q, want = %q", got, want) 814 } 815 }) 816 } 817 }