Wait before transitioning NUD entries from Probe to Failed

Wait an additional RetransmitTimer duration after the last probe before
transitioning to Failed. The previous implementation transitions immediately to
Failed after sending the last probe, which is erroneous behavior.

PiperOrigin-RevId: 338723794
This commit is contained in:
Sam Balana 2020-10-23 12:31:11 -07:00 committed by gVisor bot
parent 39e214090b
commit 8db147b554
2 changed files with 49 additions and 39 deletions

View File

@ -238,12 +238,6 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
}
retryCounter++
if retryCounter == config.MaxUnicastProbes {
e.dispatchRemoveEventLocked()
e.setStateLocked(Failed)
return
}
e.job = e.nic.stack.newJob(&e.mu, sendUnicastProbe)
e.job.Schedule(config.RetransmitTimer)
}

View File

@ -2938,10 +2938,31 @@ func TestEntryProbeToFailed(t *testing.T) {
c := DefaultNUDConfigurations()
c.MaxMulticastProbes = 3
c.MaxUnicastProbes = 3
c.DelayFirstProbeTime = c.RetransmitTimer
e, nudDisp, linkRes, clock := entryTestSetup(c)
e.mu.Lock()
e.handlePacketQueuedLocked(entryTestAddr2)
e.mu.Unlock()
{
wantProbes := []entryTestProbeInfo{
// Caused by the Unknown-to-Incomplete transition.
{
RemoteAddress: entryTestAddr1,
LocalAddress: entryTestAddr2,
},
}
linkRes.mu.Lock()
diff := cmp.Diff(linkRes.probes, wantProbes)
linkRes.probes = nil
linkRes.mu.Unlock()
if diff != "" {
t.Fatalf("link address resolver probes mismatch (-got, +want):\n%s", diff)
}
}
e.mu.Lock()
e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
Solicited: false,
Override: false,
@ -2950,36 +2971,37 @@ func TestEntryProbeToFailed(t *testing.T) {
e.handlePacketQueuedLocked(entryTestAddr2)
e.mu.Unlock()
waitFor := c.DelayFirstProbeTime + c.RetransmitTimer*time.Duration(c.MaxUnicastProbes)
clock.Advance(waitFor)
// 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,
},
}
linkRes.mu.Lock()
diff := cmp.Diff(linkRes.probes, wantProbes)
linkRes.probes = nil
linkRes.mu.Unlock()
if diff != "" {
t.Fatalf("link address resolver probe #%d mismatch (-got, +want):\n%s", i+1, diff)
}
wantProbes := []entryTestProbeInfo{
// The first probe is caused by the Unknown-to-Incomplete transition.
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: tcpip.LinkAddress(""),
LocalAddress: entryTestAddr2,
},
// The next three probe are caused by the Delay-to-Probe transition.
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: entryTestLinkAddr1,
},
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: entryTestLinkAddr1,
},
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: entryTestLinkAddr1,
},
e.mu.Lock()
if e.neigh.State != Probe {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Probe)
}
e.mu.Unlock()
}
linkRes.mu.Lock()
diff := cmp.Diff(linkRes.probes, wantProbes)
linkRes.mu.Unlock()
if diff != "" {
t.Fatalf("link address resolver probes mismatch (-got, +want):\n%s", diff)
// Wait for the last probe to expire, causing a transition to Failed.
clock.Advance(c.RetransmitTimer)
e.mu.Lock()
if e.neigh.State != Failed {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Failed)
}
e.mu.Unlock()
wantEvents := []testEntryEventInfo{
{
@ -3023,12 +3045,6 @@ func TestEntryProbeToFailed(t *testing.T) {
t.Errorf("nud dispatcher events mismatch (-got, +want):\n%s", diff)
}
nudDisp.mu.Unlock()
e.mu.Lock()
if got, want := e.neigh.State, Failed; got != want {
t.Errorf("got e.neigh.State = %q, want = %q", got, want)
}
e.mu.Unlock()
}
func TestEntryFailedGetsDeleted(t *testing.T) {