From 35a71a24f2dc5e7b36a07d054ea70e3729b4df46 Mon Sep 17 00:00:00 2001 From: Sam Balana Date: Fri, 26 Feb 2021 12:09:42 -0800 Subject: Use helper functions in neighbor entry tests Adds helper functions for transitioning into common states. This reduces the boilerplate by a fair amount, decreasing the barriers to entry for new features added to neighborEntry. PiperOrigin-RevId: 359810465 --- pkg/tcpip/stack/neighbor_entry_test.go | 3129 +++++++++----------------------- 1 file changed, 897 insertions(+), 2232 deletions(-) (limited to 'pkg/tcpip/stack') 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,24 +477,24 @@ 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, @@ -505,49 +522,46 @@ 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, @@ -558,70 +572,99 @@ 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 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() + isRouter := e.mu.isRouter + e.mu.Unlock() + if isRouter { + return fmt.Errorf("got e.mu.isRouter = %t, want = false", isRouter) + } + + return nil } func TestEntryIncompleteToReachableWithRouterFlag(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() + if err := incompleteToReachableWithRouterFlag(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("incompleteToReachableWithRouterFlag(...) = %s", err) + } +} - runImmediatelyScheduledJobs(clock) - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, +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, + }); err != nil { + return 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) + + e.mu.Lock() + isRouter := e.mu.isRouter + e.mu.Unlock() + if !isRouter { + return fmt.Errorf("got e.mu.isRouter = %t, want = true", isRouter) + } + + return nil +} + +func TestEntryIncompleteToStaleWhenUnsolicitedConfirmation(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) } 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 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 = true", e.mu.isRouter) + 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{ Addr: entryTestAddr1, LinkAddr: entryTestLinkAddr1, - State: Reachable, + State: Stale, }, }, } @@ -632,53 +675,22 @@ func TestEntryIncompleteToReachableWithRouterFlag(t *testing.T) { nudDisp.mu.Unlock() } -func TestEntryIncompleteToStaleWhenUnsolicitedConfirmation(t *testing.T) { +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() - e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{ - Solicited: false, - Override: false, - IsRouter: false, - }) + e.handleProbeLocked(entryTestLinkAddr1) 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, @@ -696,117 +708,67 @@ func TestEntryIncompleteToStaleWhenUnsolicitedConfirmation(t *testing.T) { nudDisp.mu.Unlock() } -func TestEntryIncompleteToStaleWhenProbe(t *testing.T) { +func TestEntryIncompleteToUnreachable(t *testing.T) { c := DefaultNUDConfigurations() + 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) + } +} - 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) +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) + } } - 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) - } - e.mu.Unlock() + // The first probe was sent in the transition from Unknown to Incomplete. + 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{ - 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() -} + // Observe each subsequent multicast probe transmitted. + for i := uint32(1); i < c.MaxMulticastProbes; i++ { + wantProbes := []entryTestProbeInfo{{ + RemoteAddress: entryTestAddr1, + 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) + } -func TestEntryIncompleteToUnreachable(t *testing.T) { - c := DefaultNUDConfigurations() - c.MaxMulticastProbes = 3 - e, nudDisp, linkRes, clock := entryTestSetup(c) + 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) + } - 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) + clock.Advance(c.RetransmitTimer) } - e.mu.Unlock() - waitFor := c.RetransmitTimer * time.Duration(c.MaxMulticastProbes) - clock.Advance(waitFor) - - 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, - }, - } - 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, @@ -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,57 +896,64 @@ 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, @@ -1059,76 +964,50 @@ 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, @@ -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,25 +1047,18 @@ 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, @@ -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,25 +1098,18 @@ 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, @@ -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,25 +1186,18 @@ 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, @@ -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,25 +1237,18 @@ 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, @@ -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, @@ -1578,24 +1289,6 @@ func TestEntryStaleToStaleWhenOverrideConfirmation(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, @@ -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,25 +1324,18 @@ 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, @@ -1694,59 +1357,43 @@ 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, @@ -1758,85 +1405,46 @@ 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, @@ -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,35 +1487,18 @@ 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, @@ -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,35 +1541,18 @@ 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, @@ -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,42 +1570,21 @@ 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, + Override: true, IsRouter: false, }) if e.mu.neigh.State != Delay { @@ -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,69 +1618,32 @@ 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, @@ -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,34 +1683,18 @@ 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, @@ -2335,91 +1716,120 @@ 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, - }, + 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) + } + } + + // Wait for the first unicast probe to be transmitted, marking the + // transition from Delay to Probe. + clock.Advance(c.DelayFirstProbeTime) + + { + 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: entryTestChanged, + NICID: entryTestNICID, + Entry: NeighborEntry{ + Addr: entryTestAddr1, + LinkAddr: entryTestLinkAddr1, + State: Probe, + }, + }, + } + { + 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) } } + return nil +} + +func TestEntryProbeToStaleWhenProbeWithDifferentAddress(t *testing.T) { + c := DefaultNUDConfigurations() + e, nudDisp, linkRes, clock := entryTestSetup(c) + + 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) - 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() - 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) } } wantEvents := []testEntryEventInfo{ { - EventType: entryTestAdded, + EventType: entryTestChanged, 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, + LinkAddr: entryTestLinkAddr2, + State: Stale, }, }, } @@ -2428,112 +1838,45 @@ func TestEntryDelayToProbe(t *testing.T) { t.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() } -func TestEntryProbeToStaleWhenProbeWithDifferentAddress(t *testing.T) { +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.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, @@ -2551,748 +1894,190 @@ func TestEntryProbeToStaleWhenProbeWithDifferentAddress(t *testing.T) { nudDisp.mu.Unlock() } -func TestEntryProbeToStaleWhenConfirmationWithDifferentAddress(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) + } + 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, + Override: true, IsRouter: false, }) - e.handlePacketQueuedLocked(entryTestAddr2) + if e.mu.neigh.State != Probe { + t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) + } + if got, want := e.mu.neigh.LinkAddr, entryTestLinkAddr1; got != want { + t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", got, want) + } 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{ - Addr: entryTestAddr1, - LinkAddr: entryTestLinkAddr2, - State: Stale, - }, - }, - } + // 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 TestEntryStaysProbeWhenOverrideConfirmationWithSameAddress(t *testing.T) { +func TestEntryProbeToReachableWhenSolicitedOverrideConfirmation(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) + } + if err := probeToReachableWithOverride(e, nudDisp, linkRes, clock, entryTestLinkAddr2); err != nil { + t.Fatalf("probeToReachableWithOverride(...) = %s", err) + } +} + +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() + + 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) + + if e.mu.neigh.State != Reachable { + return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable) + } + 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 + } + // No probes should have been sent. runImmediatelyScheduledJobs(clock) { - wantProbes := []entryTestProbeInfo{ - { - RemoteAddress: entryTestAddr1, - RemoteLinkAddress: tcpip.LinkAddress(""), - LocalAddress: entryTestAddr2, - }, - } linkRes.mu.Lock() - diff := cmp.Diff(wantProbes, linkRes.probes) - linkRes.probes = nil + 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() - 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) - } - } - - 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, - IsRouter: false, - }) - if e.mu.neigh.State != Probe { - t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe) - } - if got, want := e.mu.neigh.LinkAddr, entryTestLinkAddr1; got != want { - t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", got, want) - } - 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, - }, - }, - } - 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() -} - -// 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) { - 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 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: 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 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) - - 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, - 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) - } - } + return nil +} - 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{ +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, }) - 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, - 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() - if diff != "" { - t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff) - } - } - - 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) - } - } - - 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: 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) - } - } - - 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) - } - } - - 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("" /* 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, - }, - }, + + if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("unknownToStale(...) = %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 := staleToDelay(e, nudDisp, linkRes, clock); err != nil { + t.Fatalf("staleToDelay(...) = %s", err) } - nudDisp.mu.Unlock() + 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, + }) +} + +func TestEntryProbeToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) { + c := DefaultNUDConfigurations() + e, nudDisp, linkRes, clock := entryTestSetup(c) + + 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 := 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, + }) } func TestEntryProbeToUnreachable(t *testing.T) { @@ -3302,105 +2087,67 @@ 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, @@ -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) + } +} + +func unreachableToIncomplete(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error { + if err := func() error { + e.mu.Lock() + defer e.mu.Unlock() - waitFor := c.RetransmitTimer * time.Duration(c.MaxMulticastProbes) - clock.Advance(waitFor) + 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,43 +2212,13 @@ 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, @@ -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() } -- cgit v1.2.3