diff options
Diffstat (limited to 'pkg/tcpip/stack')
-rw-r--r-- | pkg/tcpip/stack/neighbor_entry_test.go | 2989 |
1 files changed, 827 insertions, 2162 deletions
diff --git a/pkg/tcpip/stack/neighbor_entry_test.go b/pkg/tcpip/stack/neighbor_entry_test.go index 47a9e2448..51c14d822 100644 --- a/pkg/tcpip/stack/neighbor_entry_test.go +++ b/pkg/tcpip/stack/neighbor_entry_test.go @@ -139,6 +139,8 @@ func (e testEntryEventInfo) String() string { // testNUDDispatcher implements NUDDispatcher to validate the dispatching of // events upon certain NUD state machine events. type testNUDDispatcher struct { + // TODO(gvisor.bug/issue/5541): Refactor to use the embedded sync.Mutex + // pattern. mu sync.Mutex events []testEntryEventInfo } @@ -176,6 +178,8 @@ func (d *testNUDDispatcher) OnNeighborRemoved(nicID tcpip.NICID, entry NeighborE } type entryTestLinkResolver struct { + // TODO(gvisor.bug/issue/5541): Refactor to use the embedded sync.Mutex + // pattern. mu sync.Mutex probes []entryTestProbeInfo } @@ -323,12 +327,27 @@ func TestEntryUnknownToIncomplete(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) + } +} + +func unknownToIncomplete(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() + + if e.mu.neigh.State != Unknown { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unknown) + } + e.handlePacketQueuedLocked(entryTestAddr2) + if e.mu.neigh.State != Incomplete { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + } + return nil + }(); err != nil { + return err } - e.mu.Unlock() runImmediatelyScheduledJobs(clock) wantProbes := []entryTestProbeInfo{ @@ -340,9 +359,10 @@ func TestEntryUnknownToIncomplete(t *testing.T) { } linkRes.mu.Lock() diff := cmp.Diff(wantProbes, linkRes.probes) + linkRes.probes = nil linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) } wantEvents := []testEntryEventInfo{ @@ -359,31 +379,50 @@ func TestEntryUnknownToIncomplete(t *testing.T) { { nudDisp.mu.Lock() diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil nudDisp.mu.Unlock() if diff != "" { - t.Fatalf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } } + return nil } func TestEntryUnknownToStale(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handleProbeLocked(entryTestLinkAddr1) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) + } +} + +func unknownToStale(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() + + if e.mu.neigh.State != Unknown { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unknown) + } + e.handleProbeLocked(entryTestLinkAddr1) + if e.mu.neigh.State != Stale { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + } + return nil + }(); err != nil { + return err } - e.mu.Unlock() // No probes should have been sent. runImmediatelyScheduledJobs(clock) - linkRes.mu.Lock() - diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } wantEvents := []testEntryEventInfo{ @@ -397,11 +436,17 @@ func TestEntryUnknownToStale(t *testing.T) { }, }, } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + { + nudDisp.mu.Lock() + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil + nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } } - nudDisp.mu.Unlock() + + return nil } func TestEntryIncompleteToIncompleteDoesNotChangeUpdatedAt(t *testing.T) { @@ -409,48 +454,20 @@ func TestEntryIncompleteToIncompleteDoesNotChangeUpdatedAt(t *testing.T) { c.MaxMulticastProbes = 3 e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - updatedAtNanos := e.mu.neigh.UpdatedAtNanos - e.mu.Unlock() - - clock.Advance(c.RetransmitTimer) // UpdatedAt should remain the same during address resolution. - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } - e.mu.Lock() - if got, want := e.mu.neigh.UpdatedAtNanos, updatedAtNanos; got != want { - t.Errorf("got e.mu.neigh.UpdatedAt = %q, want = %q", got, want) - } + updatedAtNanos := e.mu.neigh.UpdatedAtNanos e.mu.Unlock() - clock.Advance(c.RetransmitTimer) + // Wait for the rest of the reachability probe transmissions, signifying + // Incomplete to Incomplete transitions. + for i := uint32(1); i < c.MaxMulticastProbes; i++ { + clock.Advance(c.RetransmitTimer) - // UpdatedAt should change after failing address resolution. Timing out after - // sending the last probe transitions the entry to Unreachable. - { wantProbes := []entryTestProbeInfo{ { RemoteAddress: entryTestAddr1, @@ -460,25 +477,25 @@ func TestEntryIncompleteToIncompleteDoesNotChangeUpdatedAt(t *testing.T) { } linkRes.mu.Lock() diff := cmp.Diff(wantProbes, linkRes.probes) + linkRes.probes = nil linkRes.mu.Unlock() if diff != "" { t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) } + + e.mu.Lock() + if got, want := e.mu.neigh.UpdatedAtNanos, updatedAtNanos; got != want { + t.Errorf("got e.mu.neigh.UpdatedAt = %q, want = %q", got, want) + } + e.mu.Unlock() } + // UpdatedAt should change after failing address resolution. Timing out after + // sending the last probe transitions the entry to Unreachable. clock.Advance(c.RetransmitTimer) wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -505,50 +522,47 @@ func TestEntryIncompleteToReachable(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) +} + +func incompleteToReachableWithFlags(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock, flags ReachabilityConfirmationFlags) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() + + if e.mu.neigh.State != Incomplete { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + } + e.handleConfirmationLocked(entryTestLinkAddr1, flags) + if e.mu.neigh.State != Reachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + } + if e.mu.neigh.LinkAddr != entryTestLinkAddr1 { + return fmt.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1) + } + return nil + }(); err != nil { + return err } - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } - e.mu.Unlock() wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -558,104 +572,75 @@ func TestEntryIncompleteToReachable(t *testing.T) { }, }, } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + { + nudDisp.mu.Lock() + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil + nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } } - nudDisp.mu.Unlock() + + return nil } -func TestEntryIncompleteToReachableWithRouterFlag(t *testing.T) { - c := DefaultNUDConfigurations() - e, nudDisp, linkRes, clock := entryTestSetup(c) +func incompleteToReachable(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := incompleteToReachableWithFlags(e, nudDisp, linkRes, clock, ReachabilityConfirmationFlags{ + Solicited: true, + Override: false, + IsRouter: false, + }); err != nil { + return err + } e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) - } + isRouter := e.mu.isRouter e.mu.Unlock() + if isRouter { + return fmt.Errorf("got e.mu.isRouter = %t, want = false", isRouter) + } - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + return nil +} + +func TestEntryIncompleteToReachableWithRouterFlag(t *testing.T) { + c := DefaultNUDConfigurations() + e, nudDisp, linkRes, clock := entryTestSetup(c) + + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := incompleteToReachableWithRouterFlag(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachableWithRouterFlag(...) = %s", err) } +} - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ +func incompleteToReachableWithRouterFlag(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := incompleteToReachableWithFlags(e, nudDisp, linkRes, clock, ReachabilityConfirmationFlags{ Solicited: true, Override: false, IsRouter: true, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + }); err != nil { + return err } - if !e.mu.isRouter { - t.Errorf("got e.mu.isRouter = %t, want = true", e.mu.isRouter) - } - e.mu.Unlock() - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + e.mu.Lock() + isRouter := e.mu.isRouter + e.mu.Unlock() + if !isRouter { + return fmt.Errorf("got e.mu.isRouter = %t, want = true", isRouter) } - nudDisp.mu.Unlock() + + return nil } func TestEntryIncompleteToStaleWhenUnsolicitedConfirmation(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) - } - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } e.mu.Lock() @@ -667,19 +652,13 @@ func TestEntryIncompleteToStaleWhenUnsolicitedConfirmation(t *testing.T) { if e.mu.neigh.State != Stale { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) } + if e.mu.isRouter { + t.Errorf("got e.mu.isRouter = %t, want = false", e.mu.isRouter) + } e.mu.Unlock() wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -700,26 +679,8 @@ func TestEntryIncompleteToStaleWhenProbe(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) - } - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } e.mu.Lock() @@ -731,15 +692,6 @@ func TestEntryIncompleteToStaleWhenProbe(t *testing.T) { wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -761,53 +713,63 @@ func TestEntryIncompleteToUnreachable(t *testing.T) { c.MaxMulticastProbes = 3 e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - e.mu.Unlock() + if err := incompleteToUnreachable(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToUnreachable(...) = %s", err) + } +} - waitFor := c.RetransmitTimer * time.Duration(c.MaxMulticastProbes) - clock.Advance(waitFor) +func incompleteToUnreachable(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + { + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Incomplete { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Incomplete) + } + } - wantProbes := []entryTestProbeInfo{ - // The Incomplete-to-Incomplete state transition is tested here by - // verifying that 3 reachability probes were sent. - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - { + // The first probe was sent in the transition from Unknown to Incomplete. + clock.Advance(c.RetransmitTimer) + + // Observe each subsequent multicast probe transmitted. + for i := uint32(1); i < c.MaxMulticastProbes; i++ { + wantProbes := []entryTestProbeInfo{{ RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), + RemoteLinkAddress: "", LocalAddress: entryTestAddr2, - }, + }} + linkRes.mu.Lock() + diff := cmp.Diff(wantProbes, linkRes.probes) + linkRes.probes = nil + linkRes.mu.Unlock() + if diff != "" { + return fmt.Errorf("link address resolver probe #%d mismatch (-want, +got):\n%s", i+1, diff) + } + + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Incomplete { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Incomplete) + } + + clock.Advance(c.RetransmitTimer) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + + { + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Unreachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Unreachable) + } } wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -818,16 +780,14 @@ func TestEntryIncompleteToUnreachable(t *testing.T) { }, } nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil nudDisp.mu.Unlock() - - e.mu.Lock() - if e.mu.neigh.State != Unreachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unreachable) + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } - e.mu.Unlock() + + return nil } type testLocker struct{} @@ -837,122 +797,67 @@ var _ sync.Locker = (*testLocker)(nil) func (*testLocker) Lock() {} func (*testLocker) Unlock() {} -func TestEntryStaysReachableWhenConfirmationWithRouterFlag(t *testing.T) { +func TestEntryReachableToReachableClearsRouterWhenConfirmationWithoutRouter(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - ipv6EP := e.cache.nic.networkEndpoints[header.IPv6ProtocolNumber].(*testIPv6Endpoint) - - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := incompleteToReachableWithRouterFlag(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachableWithRouterFlag(...) = %s", err) } e.mu.Lock() e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, + Solicited: false, Override: false, - IsRouter: true, + IsRouter: false, }) if e.mu.neigh.State != Reachable { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) } - if got, want := e.mu.isRouter, true; got != want { - t.Errorf("got e.mu.isRouter = %t, want = %t", got, want) - } - - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) if got, want := e.mu.isRouter, false; got != want { t.Errorf("got e.mu.isRouter = %t, want = %t", got, want) } + ipv6EP := e.cache.nic.networkEndpoints[header.IPv6ProtocolNumber].(*testIPv6Endpoint) if ipv6EP.invalidatedRtr != e.mu.neigh.Addr { t.Errorf("got ipv6EP.invalidatedRtr = %s, want = %s", ipv6EP.invalidatedRtr, e.mu.neigh.Addr) } e.mu.Unlock() - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } + + // No events should have been dispatched. nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } + diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.events, eventDiffOpts()...) nudDisp.mu.Unlock() - - e.mu.Lock() - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + if diff != "" { + t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } - e.mu.Unlock() } -func TestEntryStaysReachableWhenProbeWithSameAddress(t *testing.T) { +func TestEntryReachableToReachableWhenProbeWithSameAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } e.handleProbeLocked(entryTestLinkAddr1) if e.mu.neigh.State != Reachable { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) @@ -962,31 +867,24 @@ func TestEntryStaysReachableWhenProbeWithSameAddress(t *testing.T) { } e.mu.Unlock() - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } + + // No events should have been dispatched. nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { + diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.events, eventDiffOpts()...) + nudDisp.mu.Unlock() + if diff != "" { t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } - nudDisp.mu.Unlock() } func TestEntryReachableToStaleWhenTimeout(t *testing.T) { @@ -998,58 +896,65 @@ func TestEntryReachableToStaleWhenTimeout(t *testing.T) { e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) + } + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) + } + if err := reachableToStale(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("reachableToStale(...) = %s", err) + } +} - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, +// reachableToStale transitions a neighborEntry in Reachable state to Stale +// state. Depends on the elimination of random factors in the ReachableTime +// computation. +// +// c.MinRandomFactor = 1 +// c.MaxRandomFactor = 1 +func reachableToStale(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + // Ensure there are no random factors in the ReachableTime computation. + if c.MinRandomFactor != 1 { + return fmt.Errorf("got c.MinRandomFactor = %f, want = 1", c.MinRandomFactor) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if c.MaxRandomFactor != 1 { + return fmt.Errorf("got c.MaxRandomFactor = %f, want = 1", c.MaxRandomFactor) } - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + { + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Reachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Reachable) + } } - e.mu.Unlock() clock.Advance(c.BaseReachableTime) + { + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Stale { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Stale) + } + } + + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1059,77 +964,51 @@ func TestEntryReachableToStaleWhenTimeout(t *testing.T) { }, }, } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } - nudDisp.mu.Unlock() + { - e.mu.Lock() - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + nudDisp.mu.Lock() + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil + nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } } - e.mu.Unlock() + + return nil } func TestEntryReachableToStaleWhenProbeWithDifferentAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } e.handleProbeLocked(entryTestLinkAddr2) if e.mu.neigh.State != Stale { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1150,34 +1029,14 @@ func TestEntryReachableToStaleWhenConfirmationWithDifferentAddress(t *testing.T) c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: false, Override: false, @@ -1188,26 +1047,19 @@ func TestEntryReachableToStaleWhenConfirmationWithDifferentAddress(t *testing.T) } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1228,34 +1080,14 @@ func TestEntryReachableToStaleWhenConfirmationWithDifferentAddressAndOverride(t c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: true, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: false, Override: true, @@ -1266,26 +1098,19 @@ func TestEntryReachableToStaleWhenConfirmationWithDifferentAddressAndOverride(t } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1302,38 +1127,15 @@ func TestEntryReachableToStaleWhenConfirmationWithDifferentAddressAndOverride(t nudDisp.mu.Unlock() } -func TestEntryStaysStaleWhenProbeWithSameAddress(t *testing.T) { +func TestEntryStaleToStaleWhenProbeWithSameAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } e.handleProbeLocked(entryTestLinkAddr1) if e.mu.neigh.State != Stale { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) @@ -1343,28 +1145,20 @@ func TestEntryStaysStaleWhenProbeWithSameAddress(t *testing.T) { } e.mu.Unlock() - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } + + // No events should have been dispatched. nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { + if diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.events); diff != "" { t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } nudDisp.mu.Unlock() @@ -1374,34 +1168,11 @@ func TestEntryStaleToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: true, Override: true, @@ -1415,26 +1186,19 @@ func TestEntryStaleToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1455,34 +1219,11 @@ func TestEntryStaleToReachableWhenSolicitedConfirmationWithoutAddress(t *testing c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{ Solicited: true, Override: false, @@ -1496,26 +1237,19 @@ func TestEntryStaleToReachableWhenSolicitedConfirmationWithoutAddress(t *testing } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1536,34 +1270,11 @@ func TestEntryStaleToStaleWhenOverrideConfirmation(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: false, Override: true, @@ -1579,24 +1290,6 @@ func TestEntryStaleToStaleWhenOverrideConfirmation(t *testing.T) { wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1617,34 +1310,11 @@ func TestEntryStaleToStaleWhenProbeUpdateAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } e.handleProbeLocked(entryTestLinkAddr2) if e.mu.neigh.State != Stale { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) @@ -1654,26 +1324,19 @@ func TestEntryStaleToStaleWhenProbeUpdateAddress(t *testing.T) { } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1694,60 +1357,44 @@ func TestEntryStaleToDelay(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } +} - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) +func staleToDelay(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() + + if e.mu.neigh.State != Stale { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + } + e.handlePacketQueuedLocked(entryTestAddr2) + if e.mu.neigh.State != Delay { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) + } + return nil + }(); err != nil { + return err } - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } - e.mu.Unlock() wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1758,86 +1405,47 @@ func TestEntryStaleToDelay(t *testing.T) { }, } nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } + + return nil } func TestEntryDelayToReachableWhenUpperLevelConfirmation(t *testing.T) { c := DefaultNUDConfigurations() - // Eliminate random factors from ReachableTime computation so the transition - // from Stale to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } e.handleUpperLevelConfirmationLocked() if e.mu.neigh.State != Reachable { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) } e.mu.Unlock() - clock.Advance(c.BaseReachableTime) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1846,15 +1454,6 @@ func TestEntryDelayToReachableWhenUpperLevelConfirmation(t *testing.T) { State: Reachable, }, }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, } nudDisp.mu.Lock() if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { @@ -1865,43 +1464,16 @@ func TestEntryDelayToReachableWhenUpperLevelConfirmation(t *testing.T) { func TestEntryDelayToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { c := DefaultNUDConfigurations() - c.MaxMulticastProbes = 1 - // Eliminate random factors from ReachableTime computation so the transition - // from Stale to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: true, Override: true, @@ -1915,36 +1487,19 @@ func TestEntryDelayToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { } e.mu.Unlock() - clock.Advance(c.BaseReachableTime) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -1953,15 +1508,6 @@ func TestEntryDelayToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { State: Reachable, }, }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, - State: Stale, - }, - }, } nudDisp.mu.Lock() if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { @@ -1972,43 +1518,16 @@ func TestEntryDelayToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { func TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) { c := DefaultNUDConfigurations() - c.MaxMulticastProbes = 1 - // Eliminate random factors from ReachableTime computation so the transition - // from Stale to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{ Solicited: true, Override: false, @@ -2022,36 +1541,19 @@ func TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress(t *testing } e.mu.Unlock() - clock.Advance(c.BaseReachableTime) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -2060,15 +1562,6 @@ func TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress(t *testing State: Reachable, }, }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, } nudDisp.mu.Lock() if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { @@ -2077,41 +1570,20 @@ func TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress(t *testing nudDisp.mu.Unlock() } -func TestEntryStaysDelayWhenOverrideConfirmationWithSameAddress(t *testing.T) { +func TestEntryDelayToDelayWhenOverrideConfirmationWithSameAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } e.mu.Lock() e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, Override: true, IsRouter: false, }) @@ -2123,37 +1595,20 @@ func TestEntryStaysDelayWhenOverrideConfirmationWithSameAddress(t *testing.T) { } e.mu.Unlock() - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } + + // No events should have been dispatched. nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { + if diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.events); diff != "" { t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } nudDisp.mu.Unlock() @@ -2163,70 +1618,33 @@ func TestEntryDelayToStaleWhenProbeWithDifferentAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } e.handleProbeLocked(entryTestLinkAddr2) if e.mu.neigh.State != Stale { t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -2247,35 +1665,14 @@ func TestEntryDelayToStaleWhenConfirmationWithDifferentAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: false, Override: true, @@ -2286,35 +1683,19 @@ func TestEntryDelayToStaleWhenConfirmationWithDifferentAddress(t *testing.T) { } e.mu.Unlock() + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } + } + wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -2335,85 +1716,58 @@ func TestEntryDelayToProbe(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) + } + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) + } + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) + } +} - runImmediatelyScheduledJobs(clock) +func delayToProbe(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Delay { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Delay) } } - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Delay { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay) - } - e.mu.Unlock() - + // Wait for the first unicast probe to be transmitted, marking the + // transition from Delay to Probe. clock.Advance(c.DelayFirstProbeTime) + { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Probe { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Probe) } + } + + wantProbes := []entryTestProbeInfo{ + { + RemoteAddress: entryTestAddr1, + RemoteLinkAddress: entryTestLinkAddr1, + }, + } + { linkRes.mu.Lock() diff := cmp.Diff(wantProbes, linkRes.probes) + linkRes.probes = nil linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) } } wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -2423,118 +1777,53 @@ func TestEntryDelayToProbe(t *testing.T) { }, }, } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + { + nudDisp.mu.Lock() + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil + nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } } - nudDisp.mu.Unlock() - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } - e.mu.Unlock() + return nil } func TestEntryProbeToStaleWhenProbeWithDifferentAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) + } + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) + } + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) + e.handleProbeLocked(entryTestLinkAddr2) + if e.mu.neigh.State != Stale { + t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + } e.mu.Unlock() - clock.Advance(c.DelayFirstProbeTime) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) } } - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } - e.handleProbeLocked(entryTestLinkAddr2) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } - e.mu.Unlock() - wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -2555,105 +1844,40 @@ func TestEntryProbeToStaleWhenConfirmationWithDifferentAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) + } + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) + } + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) } e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ + e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ Solicited: false, - Override: false, + Override: true, IsRouter: false, }) - e.handlePacketQueuedLocked(entryTestAddr2) + if e.mu.neigh.State != Stale { + t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + } e.mu.Unlock() - clock.Advance(c.DelayFirstProbeTime) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) } } - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } - e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ - Solicited: false, - Override: true, - IsRouter: false, - }) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) - } - e.mu.Unlock() - wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -2670,62 +1894,21 @@ func TestEntryProbeToStaleWhenConfirmationWithDifferentAddress(t *testing.T) { nudDisp.mu.Unlock() } -func TestEntryStaysProbeWhenOverrideConfirmationWithSameAddress(t *testing.T) { +func TestEntryProbeToProbeWhenOverrideConfirmationWithSameAddress(t *testing.T) { c := DefaultNUDConfigurations() e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - clock.Advance(c.DelayFirstProbeTime) - { - wantProbes := []entryTestProbeInfo{ - // The second probe is caused by the Delay-to-Probe transition. - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) + } + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) } e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ Solicited: false, Override: true, @@ -2739,560 +1922,162 @@ func TestEntryStaysProbeWhenOverrideConfirmationWithSameAddress(t *testing.T) { } e.mu.Unlock() - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) + { + linkRes.mu.Lock() + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) + linkRes.mu.Unlock() + if diff != "" { + t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) + } } + + // No events should have been dispatched. nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { + diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.events, eventDiffOpts()...) + nudDisp.mu.Unlock() + if diff != "" { t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } - nudDisp.mu.Unlock() } -// TestEntryUnknownToStaleToProbeToReachable exercises the following scenario: -// 1. Probe is received -// 2. Entry is created in Stale -// 3. Packet is queued on the entry -// 4. Entry transitions to Delay then Probe -// 5. Probe is sent -func TestEntryUnknownToStaleToProbeToReachable(t *testing.T) { +func TestEntryProbeToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { c := DefaultNUDConfigurations() - // Eliminate random factors from ReachableTime computation so the transition - // from Probe to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handleProbeLocked(entryTestLinkAddr1) - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - clock.Advance(c.DelayFirstProbeTime) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } - - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } - e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ - Solicited: true, - Override: true, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - if got, want := e.mu.neigh.LinkAddr, entryTestLinkAddr2; got != want { - t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", got, want) + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } - e.mu.Unlock() - - clock.Advance(c.BaseReachableTime) - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, - State: Reachable, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, - State: Stale, - }, - }, + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + if err := probeToReachableWithOverride(e, nudDisp, linkRes, clock, entryTestLinkAddr2); err != nil { + t.Fatalf("probeToReachableWithOverride(...) = %s", err) } - nudDisp.mu.Unlock() } -func TestEntryProbeToReachableWhenSolicitedOverrideConfirmation(t *testing.T) { - c := DefaultNUDConfigurations() - // Eliminate random factors from ReachableTime computation so the transition - // from Stale to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - - e, nudDisp, linkRes, clock := entryTestSetup(c) +func probeToReachableWithFlags(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() + prevLinkAddr := e.mu.neigh.LinkAddr + if e.mu.neigh.State != Probe { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) + } + e.handleConfirmationLocked(linkAddr, flags) - runImmediatelyScheduledJobs(clock) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, + if e.mu.neigh.State != Reachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if linkAddr == "" { + linkAddr = prevLinkAddr } + if e.mu.neigh.LinkAddr != linkAddr { + return fmt.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, linkAddr) + } + return nil + }(); err != nil { + return err } - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - clock.Advance(c.DelayFirstProbeTime) + // No probes should have been sent. + runImmediatelyScheduledJobs(clock) { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) + diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.probes) linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) } } - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } - e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{ - Solicited: true, - Override: true, - IsRouter: false, - }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } - if got, want := e.mu.neigh.LinkAddr, entryTestLinkAddr2; got != want { - t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", got, want) - } - e.mu.Unlock() - - clock.Advance(c.BaseReachableTime) wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, + LinkAddr: linkAddr, State: Reachable, }, }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, - State: Stale, - }, - }, } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } - nudDisp.mu.Unlock() -} - -func TestEntryProbeToReachableWhenSolicitedConfirmationWithSameAddress(t *testing.T) { - c := DefaultNUDConfigurations() - // Eliminate random factors from ReachableTime computation so the transition - // from Stale to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - - e, nudDisp, linkRes, clock := entryTestSetup(c) - - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() + nudDisp.mu.Lock() + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil + nudDisp.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) } } - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, + return nil +} + +func probeToReachableWithOverride(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock, linkAddr tcpip.LinkAddress) error { + return probeToReachableWithFlags(e, nudDisp, linkRes, clock, linkAddr, ReachabilityConfirmationFlags{ + Solicited: true, + Override: true, IsRouter: false, }) - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() +} - clock.Advance(c.DelayFirstProbeTime) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } - } +func TestEntryProbeToReachableWhenSolicitedConfirmationWithSameAddress(t *testing.T) { + c := DefaultNUDConfigurations() + e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) + } + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) + } + if err := probeToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("probeToReachable(...) = %s", err) + } +} + +func probeToReachable(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + return probeToReachableWithFlags(e, nudDisp, linkRes, clock, entryTestLinkAddr1, ReachabilityConfirmationFlags{ Solicited: true, Override: false, IsRouter: false, }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } - e.mu.Unlock() - - clock.Advance(c.BaseReachableTime) - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } - nudDisp.mu.Unlock() } func TestEntryProbeToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) { c := DefaultNUDConfigurations() - // Eliminate random factors from ReachableTime computation so the transition - // from Stale to Reachable will only take BaseReachableTime duration. - c.MinRandomFactor = 1 - c.MaxRandomFactor = 1 - e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - runImmediatelyScheduledJobs(clock) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) } - - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() - - clock.Advance(c.DelayFirstProbeTime) - { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } - - e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) } - e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{ + if err := probeToReachableWithoutAddress(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("probeToReachableWithoutAddress(...) = %s", err) + } +} + +func probeToReachableWithoutAddress(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + return probeToReachableWithFlags(e, nudDisp, linkRes, clock, "" /* linkAddr */, ReachabilityConfirmationFlags{ Solicited: true, Override: false, IsRouter: false, }) - if e.mu.neigh.State != Reachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) - } - e.mu.Unlock() - - clock.Advance(c.BaseReachableTime) - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Reachable, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } - nudDisp.mu.Unlock() } func TestEntryProbeToUnreachable(t *testing.T) { @@ -3302,106 +2087,68 @@ func TestEntryProbeToUnreachable(t *testing.T) { c.DelayFirstProbeTime = c.RetransmitTimer e, nudDisp, linkRes, clock := entryTestSetup(c) - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %s", err) + } + if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) + } + if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("delayToProbe(...) = %s", err) + } + if err := probeToUnreachable(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("probeToUnreachable(...) = %s", err) + } +} - runImmediatelyScheduledJobs(clock) +func probeToUnreachable(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - LocalAddress: entryTestAddr2, - }, - } - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Probe { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Probe) } } - e.mu.Lock() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) - e.handlePacketQueuedLocked(entryTestAddr2) - e.mu.Unlock() + // The first probe was sent in the transition from Delay to Probe. + clock.Advance(c.RetransmitTimer) - // Observe each probe sent while in the Probe state. - for i := uint32(0); i < c.MaxUnicastProbes; i++ { - clock.Advance(c.RetransmitTimer) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: entryTestLinkAddr1, - }, - } + // Observe each subsequent unicast probe transmitted. + for i := uint32(1); i < c.MaxUnicastProbes; i++ { + wantProbes := []entryTestProbeInfo{{ + RemoteAddress: entryTestAddr1, + RemoteLinkAddress: entryTestLinkAddr1, + }} linkRes.mu.Lock() diff := cmp.Diff(wantProbes, linkRes.probes) linkRes.probes = nil linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probe #%d mismatch (-want, +got):\n%s", i+1, diff) + return fmt.Errorf("link address resolver probe #%d mismatch (-want, +got):\n%s", i+1, diff) } e.mu.Lock() - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } + state := e.mu.neigh.State e.mu.Unlock() + if state != Probe { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Probe) + } + + clock.Advance(c.RetransmitTimer) } - // Wait for the last probe to expire, causing a transition to Unreachable. - clock.Advance(c.RetransmitTimer) - e.mu.Lock() - if e.mu.neigh.State != Unreachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unreachable) + { + e.mu.Lock() + state := e.mu.neigh.State + e.mu.Unlock() + if state != Unreachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Unreachable) + } } - e.mu.Unlock() wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Stale, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Delay, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr1, - State: Probe, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -3412,10 +2159,14 @@ func TestEntryProbeToUnreachable(t *testing.T) { }, } nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) - } + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } + + return nil } func TestEntryUnreachableToIncomplete(t *testing.T) { @@ -3423,31 +2174,36 @@ func TestEntryUnreachableToIncomplete(t *testing.T) { c.MaxMulticastProbes = 3 e, nudDisp, linkRes, clock := entryTestSetup(c) - // TODO(gvisor.dev/issue/4872): Use helper functions to start entry tests in - // their expected state. - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - e.mu.Unlock() + if err := incompleteToUnreachable(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToUnreachable(...) = %s", err) + } + if err := unreachableToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unreachableToIncomplete(...) = %s", err) + } +} - waitFor := c.RetransmitTimer * time.Duration(c.MaxMulticastProbes) - clock.Advance(waitFor) +func unreachableToIncomplete(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() + + if e.mu.neigh.State != Unreachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unreachable) + } + e.handlePacketQueuedLocked(entryTestAddr2) + if e.mu.neigh.State != Incomplete { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + } + return nil + }(); err != nil { + return err + } + runImmediatelyScheduledJobs(clock) wantProbes := []entryTestProbeInfo{ - // The Incomplete-to-Incomplete state transition is tested here by - // verifying that 3 reachability probes were sent. - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, { RemoteAddress: entryTestAddr1, RemoteLinkAddress: tcpip.LinkAddress(""), @@ -3456,44 +2212,14 @@ func TestEntryUnreachableToIncomplete(t *testing.T) { } linkRes.mu.Lock() diff := cmp.Diff(wantProbes, linkRes.probes) + linkRes.probes = nil linkRes.mu.Unlock() if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } - - e.mu.Lock() - if e.mu.neigh.State != Unreachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unreachable) - } - e.mu.Unlock() - - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) + return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff) } - e.mu.Unlock() wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Unreachable, - }, - }, - { EventType: entryTestChanged, NICID: entryTestNICID, Entry: NeighborEntry{ @@ -3503,102 +2229,41 @@ func TestEntryUnreachableToIncomplete(t *testing.T) { }, }, } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + { + nudDisp.mu.Lock() + diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...) + nudDisp.events = nil + nudDisp.mu.Unlock() + if diff != "" { + return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + } } - nudDisp.mu.Unlock() + return nil } func TestEntryUnreachableToStale(t *testing.T) { - wantProbes := []entryTestProbeInfo{ - // The Incomplete-to-Incomplete state transition is tested here by - // verifying that 3 reachability probes were sent. - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } - c := DefaultNUDConfigurations() - c.MaxMulticastProbes = uint32(len(wantProbes)) - e, nudDisp, linkRes, clock := entryTestSetup(c) - - // TODO(gvisor.dev/issue/4872): Use helper functions to start entry tests in - // their expected state. - e.mu.Lock() - e.handlePacketQueuedLocked(entryTestAddr2) - if e.mu.neigh.State != Incomplete { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete) - } - e.mu.Unlock() + c.MaxMulticastProbes = 3 + // Eliminate random factors from ReachableTime computation so the transition + // from Stale to Reachable will only take BaseReachableTime duration. + c.MinRandomFactor = 1 + c.MaxRandomFactor = 1 - waitFor := c.RetransmitTimer * time.Duration(c.MaxMulticastProbes) - clock.Advance(waitFor) + e, nudDisp, linkRes, clock := entryTestSetup(c) - linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.mu.Unlock() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) + if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToIncomplete(...) = %s", err) } - - e.mu.Lock() - if e.mu.neigh.State != Unreachable { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unreachable) + if err := incompleteToUnreachable(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToUnreachable(...) = %s", err) } - e.mu.Unlock() - - e.mu.Lock() - e.handleProbeLocked(entryTestLinkAddr2) - if e.mu.neigh.State != Stale { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale) + if err := unreachableToIncomplete(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unreachableToIncomplete(...) = %s", err) } - e.mu.Unlock() - - wantEvents := []testEntryEventInfo{ - { - EventType: entryTestAdded, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Incomplete, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: tcpip.LinkAddress(""), - State: Unreachable, - }, - }, - { - EventType: entryTestChanged, - NICID: entryTestNICID, - Entry: NeighborEntry{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, - State: Stale, - }, - }, + if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachable(...) = %s", err) } - nudDisp.mu.Lock() - if diff := cmp.Diff(wantEvents, nudDisp.events, eventDiffOpts()...); diff != "" { - t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff) + if err := reachableToStale(c, e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("reachableToStale(...) = %s", err) } - nudDisp.mu.Unlock() } |