github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 "github.com/SagerNet/gvisor/pkg/tcpip" 25 "github.com/SagerNet/gvisor/pkg/tcpip/faketime" 26 "github.com/SagerNet/gvisor/pkg/tcpip/link/channel" 27 "github.com/SagerNet/gvisor/pkg/tcpip/network/ipv6" 28 "github.com/SagerNet/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.Neigbors(%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.Now()}}, 174 neighbors, 175 ); diff != "" { 176 t.Errorf("neighbors mismatch (-want +got):\n%s", diff) 177 } 178 } 179 } 180 181 { 182 err := s.ClearNeighbors(test.nicID, ipv6.ProtocolNumber) 183 if diff := cmp.Diff(test.expectedErr, err); diff != "" { 184 t.Errorf("s.ClearNeigbors(%d, %d) error mismatch (-want +got):\n%s", test.nicID, ipv6.ProtocolNumber, diff) 185 } else if test.expectedErr == nil { 186 if neighbors, err := s.Neighbors(test.nicID, ipv6.ProtocolNumber); err != nil { 187 t.Errorf("s.Neighbors(%d, %d): %s", test.nicID, ipv6.ProtocolNumber, err) 188 } else if len(neighbors) != 0 { 189 t.Errorf("got len(neighbors) = %d, want = 0; neighbors = %#v", len(neighbors), neighbors) 190 } 191 } 192 } 193 }) 194 } 195 } 196 197 func TestDefaultNUDConfigurations(t *testing.T) { 198 const nicID = 1 199 200 e := channel.New(0, 1280, linkAddr1) 201 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 202 203 s := stack.New(stack.Options{ 204 // A neighbor cache is required to store NUDConfigurations. The networking 205 // stack will only allocate neighbor caches if a protocol providing link 206 // address resolution is specified (e.g. ARP or IPv6). 207 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 208 NUDConfigs: stack.DefaultNUDConfigurations(), 209 }) 210 if err := s.CreateNIC(nicID, e); err != nil { 211 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 212 } 213 c, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 214 if err != nil { 215 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 216 } 217 if got, want := c, stack.DefaultNUDConfigurations(); got != want { 218 t.Errorf("got stack.NUDConfigurations(%d, %d) = %+v, want = %+v", nicID, ipv6.ProtocolNumber, got, want) 219 } 220 } 221 222 func TestNUDConfigurationsBaseReachableTime(t *testing.T) { 223 tests := []struct { 224 name string 225 baseReachableTime time.Duration 226 want time.Duration 227 }{ 228 // Invalid cases 229 { 230 name: "EqualToZero", 231 baseReachableTime: 0, 232 want: defaultBaseReachableTime, 233 }, 234 // Valid cases 235 { 236 name: "MoreThanZero", 237 baseReachableTime: time.Millisecond, 238 want: time.Millisecond, 239 }, 240 { 241 name: "MoreThanDefaultBaseReachableTime", 242 baseReachableTime: 2 * defaultBaseReachableTime, 243 want: 2 * defaultBaseReachableTime, 244 }, 245 } 246 247 for _, test := range tests { 248 t.Run(test.name, func(t *testing.T) { 249 const nicID = 1 250 251 c := stack.DefaultNUDConfigurations() 252 c.BaseReachableTime = test.baseReachableTime 253 254 e := channel.New(0, 1280, linkAddr1) 255 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 256 257 s := stack.New(stack.Options{ 258 // A neighbor cache is required to store NUDConfigurations. The 259 // networking stack will only allocate neighbor caches if a protocol 260 // providing link address resolution is specified (e.g. ARP or IPv6). 261 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 262 NUDConfigs: c, 263 }) 264 if err := s.CreateNIC(nicID, e); err != nil { 265 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 266 } 267 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 268 if err != nil { 269 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 270 } 271 if got := sc.BaseReachableTime; got != test.want { 272 t.Errorf("got BaseReachableTime = %q, want = %q", got, test.want) 273 } 274 }) 275 } 276 } 277 278 func TestNUDConfigurationsMinRandomFactor(t *testing.T) { 279 tests := []struct { 280 name string 281 minRandomFactor float32 282 want float32 283 }{ 284 // Invalid cases 285 { 286 name: "LessThanZero", 287 minRandomFactor: -1, 288 want: defaultMinRandomFactor, 289 }, 290 { 291 name: "EqualToZero", 292 minRandomFactor: 0, 293 want: defaultMinRandomFactor, 294 }, 295 // Valid cases 296 { 297 name: "MoreThanZero", 298 minRandomFactor: 1, 299 want: 1, 300 }, 301 } 302 303 for _, test := range tests { 304 t.Run(test.name, func(t *testing.T) { 305 const nicID = 1 306 307 c := stack.DefaultNUDConfigurations() 308 c.MinRandomFactor = test.minRandomFactor 309 310 e := channel.New(0, 1280, linkAddr1) 311 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 312 313 s := stack.New(stack.Options{ 314 // A neighbor cache is required to store NUDConfigurations. The 315 // networking stack will only allocate neighbor caches if a protocol 316 // providing link address resolution is specified (e.g. ARP or IPv6). 317 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 318 NUDConfigs: c, 319 }) 320 if err := s.CreateNIC(nicID, e); err != nil { 321 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 322 } 323 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 324 if err != nil { 325 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 326 } 327 if got := sc.MinRandomFactor; got != test.want { 328 t.Errorf("got MinRandomFactor = %f, want = %f", got, test.want) 329 } 330 }) 331 } 332 } 333 334 func TestNUDConfigurationsMaxRandomFactor(t *testing.T) { 335 tests := []struct { 336 name string 337 minRandomFactor float32 338 maxRandomFactor float32 339 want float32 340 }{ 341 // Invalid cases 342 { 343 name: "LessThanZero", 344 minRandomFactor: defaultMinRandomFactor, 345 maxRandomFactor: -1, 346 want: defaultMaxRandomFactor, 347 }, 348 { 349 name: "EqualToZero", 350 minRandomFactor: defaultMinRandomFactor, 351 maxRandomFactor: 0, 352 want: defaultMaxRandomFactor, 353 }, 354 { 355 name: "LessThanMinRandomFactor", 356 minRandomFactor: defaultMinRandomFactor, 357 maxRandomFactor: defaultMinRandomFactor * 0.99, 358 want: defaultMaxRandomFactor, 359 }, 360 { 361 name: "MoreThanMinRandomFactorWhenMinRandomFactorIsLargerThanMaxRandomFactorDefault", 362 minRandomFactor: defaultMaxRandomFactor * 2, 363 maxRandomFactor: defaultMaxRandomFactor, 364 want: defaultMaxRandomFactor * 6, 365 }, 366 // Valid cases 367 { 368 name: "EqualToMinRandomFactor", 369 minRandomFactor: defaultMinRandomFactor, 370 maxRandomFactor: defaultMinRandomFactor, 371 want: defaultMinRandomFactor, 372 }, 373 { 374 name: "MoreThanMinRandomFactor", 375 minRandomFactor: defaultMinRandomFactor, 376 maxRandomFactor: defaultMinRandomFactor * 1.1, 377 want: defaultMinRandomFactor * 1.1, 378 }, 379 } 380 381 for _, test := range tests { 382 t.Run(test.name, func(t *testing.T) { 383 const nicID = 1 384 385 c := stack.DefaultNUDConfigurations() 386 c.MinRandomFactor = test.minRandomFactor 387 c.MaxRandomFactor = test.maxRandomFactor 388 389 e := channel.New(0, 1280, linkAddr1) 390 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 391 392 s := stack.New(stack.Options{ 393 // A neighbor cache is required to store NUDConfigurations. The 394 // networking stack will only allocate neighbor caches if a protocol 395 // providing link address resolution is specified (e.g. ARP or IPv6). 396 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 397 NUDConfigs: c, 398 }) 399 if err := s.CreateNIC(nicID, e); err != nil { 400 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 401 } 402 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 403 if err != nil { 404 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 405 } 406 if got := sc.MaxRandomFactor; got != test.want { 407 t.Errorf("got MaxRandomFactor = %f, want = %f", got, test.want) 408 } 409 }) 410 } 411 } 412 413 func TestNUDConfigurationsRetransmitTimer(t *testing.T) { 414 tests := []struct { 415 name string 416 retransmitTimer time.Duration 417 want time.Duration 418 }{ 419 // Invalid cases 420 { 421 name: "EqualToZero", 422 retransmitTimer: 0, 423 want: defaultRetransmitTimer, 424 }, 425 { 426 name: "LessThanMinimumRetransmitTimer", 427 retransmitTimer: minimumRetransmitTimer - time.Nanosecond, 428 want: defaultRetransmitTimer, 429 }, 430 // Valid cases 431 { 432 name: "EqualToMinimumRetransmitTimer", 433 retransmitTimer: minimumRetransmitTimer, 434 want: minimumBaseReachableTime, 435 }, 436 { 437 name: "LargetThanMinimumRetransmitTimer", 438 retransmitTimer: 2 * minimumBaseReachableTime, 439 want: 2 * minimumBaseReachableTime, 440 }, 441 } 442 443 for _, test := range tests { 444 t.Run(test.name, func(t *testing.T) { 445 const nicID = 1 446 447 c := stack.DefaultNUDConfigurations() 448 c.RetransmitTimer = test.retransmitTimer 449 450 e := channel.New(0, 1280, linkAddr1) 451 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 452 453 s := stack.New(stack.Options{ 454 // A neighbor cache is required to store NUDConfigurations. The 455 // networking stack will only allocate neighbor caches if a protocol 456 // providing link address resolution is specified (e.g. ARP or IPv6). 457 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 458 NUDConfigs: c, 459 }) 460 if err := s.CreateNIC(nicID, e); err != nil { 461 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 462 } 463 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 464 if err != nil { 465 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 466 } 467 if got := sc.RetransmitTimer; got != test.want { 468 t.Errorf("got RetransmitTimer = %q, want = %q", got, test.want) 469 } 470 }) 471 } 472 } 473 474 func TestNUDConfigurationsDelayFirstProbeTime(t *testing.T) { 475 tests := []struct { 476 name string 477 delayFirstProbeTime time.Duration 478 want time.Duration 479 }{ 480 // Invalid cases 481 { 482 name: "EqualToZero", 483 delayFirstProbeTime: 0, 484 want: defaultDelayFirstProbeTime, 485 }, 486 // Valid cases 487 { 488 name: "MoreThanZero", 489 delayFirstProbeTime: time.Millisecond, 490 want: time.Millisecond, 491 }, 492 } 493 494 for _, test := range tests { 495 t.Run(test.name, func(t *testing.T) { 496 const nicID = 1 497 498 c := stack.DefaultNUDConfigurations() 499 c.DelayFirstProbeTime = test.delayFirstProbeTime 500 501 e := channel.New(0, 1280, linkAddr1) 502 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 503 504 s := stack.New(stack.Options{ 505 // A neighbor cache is required to store NUDConfigurations. The 506 // networking stack will only allocate neighbor caches if a protocol 507 // providing link address resolution is specified (e.g. ARP or IPv6). 508 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 509 NUDConfigs: c, 510 }) 511 if err := s.CreateNIC(nicID, e); err != nil { 512 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 513 } 514 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 515 if err != nil { 516 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 517 } 518 if got := sc.DelayFirstProbeTime; got != test.want { 519 t.Errorf("got DelayFirstProbeTime = %q, want = %q", got, test.want) 520 } 521 }) 522 } 523 } 524 525 func TestNUDConfigurationsMaxMulticastProbes(t *testing.T) { 526 tests := []struct { 527 name string 528 maxMulticastProbes uint32 529 want uint32 530 }{ 531 // Invalid cases 532 { 533 name: "EqualToZero", 534 maxMulticastProbes: 0, 535 want: defaultMaxMulticastProbes, 536 }, 537 // Valid cases 538 { 539 name: "MoreThanZero", 540 maxMulticastProbes: 1, 541 want: 1, 542 }, 543 } 544 545 for _, test := range tests { 546 t.Run(test.name, func(t *testing.T) { 547 const nicID = 1 548 549 c := stack.DefaultNUDConfigurations() 550 c.MaxMulticastProbes = test.maxMulticastProbes 551 552 e := channel.New(0, 1280, linkAddr1) 553 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 554 555 s := stack.New(stack.Options{ 556 // A neighbor cache is required to store NUDConfigurations. The 557 // networking stack will only allocate neighbor caches if a protocol 558 // providing link address resolution is specified (e.g. ARP or IPv6). 559 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 560 NUDConfigs: c, 561 }) 562 if err := s.CreateNIC(nicID, e); err != nil { 563 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 564 } 565 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 566 if err != nil { 567 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 568 } 569 if got := sc.MaxMulticastProbes; got != test.want { 570 t.Errorf("got MaxMulticastProbes = %q, want = %q", got, test.want) 571 } 572 }) 573 } 574 } 575 576 func TestNUDConfigurationsMaxUnicastProbes(t *testing.T) { 577 tests := []struct { 578 name string 579 maxUnicastProbes uint32 580 want uint32 581 }{ 582 // Invalid cases 583 { 584 name: "EqualToZero", 585 maxUnicastProbes: 0, 586 want: defaultMaxUnicastProbes, 587 }, 588 // Valid cases 589 { 590 name: "MoreThanZero", 591 maxUnicastProbes: 1, 592 want: 1, 593 }, 594 } 595 596 for _, test := range tests { 597 t.Run(test.name, func(t *testing.T) { 598 const nicID = 1 599 600 c := stack.DefaultNUDConfigurations() 601 c.MaxUnicastProbes = test.maxUnicastProbes 602 603 e := channel.New(0, 1280, linkAddr1) 604 e.LinkEPCapabilities |= stack.CapabilityResolutionRequired 605 606 s := stack.New(stack.Options{ 607 // A neighbor cache is required to store NUDConfigurations. The 608 // networking stack will only allocate neighbor caches if a protocol 609 // providing link address resolution is specified (e.g. ARP or IPv6). 610 NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, 611 NUDConfigs: c, 612 }) 613 if err := s.CreateNIC(nicID, e); err != nil { 614 t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) 615 } 616 sc, err := s.NUDConfigurations(nicID, ipv6.ProtocolNumber) 617 if err != nil { 618 t.Fatalf("got stack.NUDConfigurations(%d, %d) = %s", nicID, ipv6.ProtocolNumber, err) 619 } 620 if got := sc.MaxUnicastProbes; got != test.want { 621 t.Errorf("got MaxUnicastProbes = %q, want = %q", got, test.want) 622 } 623 }) 624 } 625 } 626 627 // TestNUDStateReachableTime verifies the correctness of the ReachableTime 628 // computation. 629 func TestNUDStateReachableTime(t *testing.T) { 630 tests := []struct { 631 name string 632 baseReachableTime time.Duration 633 minRandomFactor float32 634 maxRandomFactor float32 635 want time.Duration 636 }{ 637 { 638 name: "AllZeros", 639 baseReachableTime: 0, 640 minRandomFactor: 0, 641 maxRandomFactor: 0, 642 want: 0, 643 }, 644 { 645 name: "ZeroMaxRandomFactor", 646 baseReachableTime: time.Second, 647 minRandomFactor: 0, 648 maxRandomFactor: 0, 649 want: 0, 650 }, 651 { 652 name: "ZeroMinRandomFactor", 653 baseReachableTime: time.Second, 654 minRandomFactor: 0, 655 maxRandomFactor: 1, 656 want: time.Duration(defaultFakeRandomNum * float32(time.Second)), 657 }, 658 { 659 name: "FractionalRandomFactor", 660 baseReachableTime: time.Duration(math.MaxInt64), 661 minRandomFactor: 0.001, 662 maxRandomFactor: 0.002, 663 want: time.Duration((0.001 + (0.001 * defaultFakeRandomNum)) * float32(math.MaxInt64)), 664 }, 665 { 666 name: "MinAndMaxRandomFactorsEqual", 667 baseReachableTime: time.Second, 668 minRandomFactor: 1, 669 maxRandomFactor: 1, 670 want: time.Second, 671 }, 672 { 673 name: "MinAndMaxRandomFactorsDifferent", 674 baseReachableTime: time.Second, 675 minRandomFactor: 1, 676 maxRandomFactor: 2, 677 want: time.Duration((1.0 + defaultFakeRandomNum) * float32(time.Second)), 678 }, 679 { 680 name: "MaxInt64", 681 baseReachableTime: time.Duration(math.MaxInt64), 682 minRandomFactor: 1, 683 maxRandomFactor: 1, 684 want: time.Duration(math.MaxInt64), 685 }, 686 { 687 name: "Overflow", 688 baseReachableTime: time.Duration(math.MaxInt64), 689 minRandomFactor: 1.5, 690 maxRandomFactor: 1.5, 691 want: time.Duration(math.MaxInt64), 692 }, 693 { 694 name: "DoubleOverflow", 695 baseReachableTime: time.Duration(math.MaxInt64), 696 minRandomFactor: 2.5, 697 maxRandomFactor: 2.5, 698 want: time.Duration(math.MaxInt64), 699 }, 700 } 701 702 for _, test := range tests { 703 t.Run(test.name, func(t *testing.T) { 704 c := stack.NUDConfigurations{ 705 BaseReachableTime: test.baseReachableTime, 706 MinRandomFactor: test.minRandomFactor, 707 MaxRandomFactor: test.maxRandomFactor, 708 } 709 // A fake random number generator is used to ensure deterministic 710 // results. 711 rng := fakeRand{ 712 num: defaultFakeRandomNum, 713 } 714 var clock faketime.NullClock 715 s := stack.NewNUDState(c, &clock, rand.New(&rng)) 716 if got, want := s.ReachableTime(), test.want; got != want { 717 t.Errorf("got ReachableTime = %q, want = %q", got, want) 718 } 719 }) 720 } 721 } 722 723 // TestNUDStateRecomputeReachableTime exercises the ReachableTime function 724 // twice to verify recomputation of reachable time when the min random factor, 725 // max random factor, or base reachable time changes. 726 func TestNUDStateRecomputeReachableTime(t *testing.T) { 727 const defaultBase = time.Second 728 const defaultMin = 2.0 * defaultMaxRandomFactor 729 const defaultMax = 3.0 * defaultMaxRandomFactor 730 731 tests := []struct { 732 name string 733 baseReachableTime time.Duration 734 minRandomFactor float32 735 maxRandomFactor float32 736 want time.Duration 737 }{ 738 { 739 name: "BaseReachableTime", 740 baseReachableTime: 2 * defaultBase, 741 minRandomFactor: defaultMin, 742 maxRandomFactor: defaultMax, 743 want: time.Duration((defaultMin + (defaultMax-defaultMin)*defaultFakeRandomNum) * float32(2*defaultBase)), 744 }, 745 { 746 name: "MinRandomFactor", 747 baseReachableTime: defaultBase, 748 minRandomFactor: defaultMax, 749 maxRandomFactor: defaultMax, 750 want: time.Duration(defaultMax * float32(defaultBase)), 751 }, 752 { 753 name: "MaxRandomFactor", 754 baseReachableTime: defaultBase, 755 minRandomFactor: defaultMin, 756 maxRandomFactor: defaultMin, 757 want: time.Duration(defaultMin * float32(defaultBase)), 758 }, 759 { 760 name: "BothRandomFactor", 761 baseReachableTime: defaultBase, 762 minRandomFactor: 2 * defaultMin, 763 maxRandomFactor: 2 * defaultMax, 764 want: time.Duration((2*defaultMin + (2*defaultMax-2*defaultMin)*defaultFakeRandomNum) * float32(defaultBase)), 765 }, 766 { 767 name: "BaseReachableTimeAndBothRandomFactors", 768 baseReachableTime: 2 * defaultBase, 769 minRandomFactor: 2 * defaultMin, 770 maxRandomFactor: 2 * defaultMax, 771 want: time.Duration((2*defaultMin + (2*defaultMax-2*defaultMin)*defaultFakeRandomNum) * float32(2*defaultBase)), 772 }, 773 } 774 775 for _, test := range tests { 776 t.Run(test.name, func(t *testing.T) { 777 c := stack.DefaultNUDConfigurations() 778 c.BaseReachableTime = defaultBase 779 c.MinRandomFactor = defaultMin 780 c.MaxRandomFactor = defaultMax 781 782 // A fake random number generator is used to ensure deterministic 783 // results. 784 rng := fakeRand{ 785 num: defaultFakeRandomNum, 786 } 787 var clock faketime.NullClock 788 s := stack.NewNUDState(c, &clock, rand.New(&rng)) 789 old := s.ReachableTime() 790 791 if got, want := s.ReachableTime(), old; got != want { 792 t.Errorf("got ReachableTime = %q, want = %q", got, want) 793 } 794 795 // Check for recomputation when changing the min random factor, the max 796 // random factor, the base reachability time, or any permutation of those 797 // three options. 798 c.BaseReachableTime = test.baseReachableTime 799 c.MinRandomFactor = test.minRandomFactor 800 c.MaxRandomFactor = test.maxRandomFactor 801 s.SetConfig(c) 802 803 if got, want := s.ReachableTime(), test.want; got != want { 804 t.Errorf("got ReachableTime = %q, want = %q", got, want) 805 } 806 807 // Verify that ReachableTime isn't recomputed when none of the 808 // configuration options change. The random factor is changed so that if 809 // a recompution were to occur, ReachableTime would change. 810 rng.num = defaultFakeRandomNum / 2.0 811 if got, want := s.ReachableTime(), test.want; got != want { 812 t.Errorf("got ReachableTime = %q, want = %q", got, want) 813 } 814 }) 815 } 816 }