Fix panic during Address Resolution of neighbor entry created by NS

When a Neighbor Solicitation is received, a neighbor entry is created with the
remote host's link layer address, but without a link layer address resolver. If
the host decides to send a packet addressed to the IP address of that neighbor
entry, Address Resolution starts with a nil pointer to the link layer address
resolver. This causes the netstack to panic and crash.

This change ensures that when a packet is sent in that situation, the link
layer address resolver will be set before Address Resolution begins.

Tests:
 pkg/tcpip/stack:stack_test
 + TestEntryUnknownToStaleToProbeToReachable
 - TestNeighborCacheEntryNoLinkAddress

Updates #1889
Updates #1894
Updates #1895
Updates #1947
Updates #1948
Updates #1949
Updates #1950

PiperOrigin-RevId: 325516471
This commit is contained in:
Sam Balana
2020-08-07 15:07:33 -07:00
committed by gVisor bot
parent 7b9bfc0ce0
commit 94447aeab3
4 changed files with 115 additions and 43 deletions
+10 -12
View File
@@ -115,17 +115,15 @@ func (n *neighborCache) getOrCreateEntry(remoteAddr, localAddr tcpip.Address, li
// channel is returned for the top level caller to block. Channel is closed
// once address resolution is complete (success or not).
func (n *neighborCache) entry(remoteAddr, localAddr tcpip.Address, linkRes LinkAddressResolver, w *sleep.Waker) (NeighborEntry, <-chan struct{}, *tcpip.Error) {
if linkRes != nil {
if linkAddr, ok := linkRes.ResolveStaticAddress(remoteAddr); ok {
e := NeighborEntry{
Addr: remoteAddr,
LocalAddr: localAddr,
LinkAddr: linkAddr,
State: Static,
UpdatedAt: time.Now(),
}
return e, nil, nil
if linkAddr, ok := linkRes.ResolveStaticAddress(remoteAddr); ok {
e := NeighborEntry{
Addr: remoteAddr,
LocalAddr: localAddr,
LinkAddr: linkAddr,
State: Static,
UpdatedAt: time.Now(),
}
return e, nil, nil
}
entry := n.getOrCreateEntry(remoteAddr, localAddr, linkRes)
@@ -289,8 +287,8 @@ func (n *neighborCache) setConfig(config NUDConfigurations) {
// HandleProbe implements NUDHandler.HandleProbe by following the logic defined
// in RFC 4861 section 7.2.3. Validation of the probe is expected to be handled
// by the caller.
func (n *neighborCache) HandleProbe(remoteAddr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress) {
entry := n.getOrCreateEntry(remoteAddr, localAddr, nil)
func (n *neighborCache) HandleProbe(remoteAddr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress, linkRes LinkAddressResolver) {
entry := n.getOrCreateEntry(remoteAddr, localAddr, linkRes)
entry.mu.Lock()
entry.handleProbeLocked(remoteLinkAddr)
entry.mu.Unlock()
+3 -29
View File
@@ -335,32 +335,6 @@ func TestNeighborCacheEntry(t *testing.T) {
}
}
// TestNeighborCacheEntryNoLinkAddress verifies calling entry() without a
// LinkAddressResolver returns ErrNoLinkAddress.
func TestNeighborCacheEntryNoLinkAddress(t *testing.T) {
nudDisp := testNUDDispatcher{}
c := DefaultNUDConfigurations()
clock := newFakeClock()
neigh := newTestNeighborCache(&nudDisp, c, clock)
store := newTestEntryStore()
entry, ok := store.entry(0)
if !ok {
t.Fatalf("store.entry(0) not found")
}
_, _, err := neigh.entry(entry.Addr, entry.LocalAddr, nil, nil)
if err != tcpip.ErrNoLinkAddress {
t.Errorf("got neigh.entry(%s, %s, nil, nil) = %v, want = %s", entry.Addr, entry.LocalAddr, err, tcpip.ErrNoLinkAddress)
}
// No events should have been dispatched.
nudDisp.mu.Lock()
defer nudDisp.mu.Unlock()
if diff := cmp.Diff(nudDisp.events, []testEntryEventInfo(nil)); diff != "" {
t.Errorf("nud dispatcher events mismatch (-got, +want):\n%s", diff)
}
}
func TestNeighborCacheRemoveEntry(t *testing.T) {
config := DefaultNUDConfigurations()
@@ -1048,9 +1022,9 @@ func TestNeighborCacheAddStaticEntryThenOverflow(t *testing.T) {
t.Fatalf("c.store.entry(0) not found")
}
c.neigh.addStaticEntry(entry.Addr, entry.LinkAddr)
e, _, err := c.neigh.entry(entry.Addr, "", nil, nil)
e, _, err := c.neigh.entry(entry.Addr, "", c.linkRes, nil)
if err != nil {
t.Errorf("unexpected error from c.neigh.entry(%s, \"\", nil nil): %s", entry.Addr, err)
t.Errorf("unexpected error from c.neigh.entry(%s, \"\", _, nil): %s", entry.Addr, err)
}
want := NeighborEntry{
Addr: entry.Addr,
@@ -1059,7 +1033,7 @@ func TestNeighborCacheAddStaticEntryThenOverflow(t *testing.T) {
State: Static,
}
if diff := cmp.Diff(e, want, entryDiffOpts()...); diff != "" {
t.Errorf("c.neigh.entry(%s, \"\", nil, nil) mismatch (-got, +want):\n%s", entry.Addr, diff)
t.Errorf("c.neigh.entry(%s, \"\", _, nil) mismatch (-got, +want):\n%s", entry.Addr, diff)
}
wantEvents := []testEntryEventInfo{
+101 -1
View File
@@ -236,7 +236,7 @@ func entryTestSetup(c NUDConfigurations) (*neighborEntry, *testNUDDispatcher, *e
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
nudState := NewNUDState(c, rng)
linkRes := entryTestLinkResolver{}
entry := newNeighborEntry(&nic, entryTestAddr1, entryTestAddr2, nudState, &linkRes)
entry := newNeighborEntry(&nic, entryTestAddr1 /* remoteAddr */, entryTestAddr2 /* localAddr */, nudState, &linkRes)
// Stub out ndpState to verify modification of default routers.
nic.mu.ndp = ndpState{
@@ -2344,6 +2344,106 @@ func TestEntryStaysProbeWhenOverrideConfirmationWithSameAddress(t *testing.T) {
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()
e.mu.Unlock()
clock.advance(c.DelayFirstProbeTime)
wantProbes := []entryTestProbeInfo{
// Probe caused by the Delay-to-Probe transition
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: entryTestLinkAddr1,
LocalAddress: entryTestAddr2,
},
}
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)
}
e.mu.Lock()
if got, want := e.neigh.State, Probe; got != want {
t.Errorf("got e.neigh.State = %q, want = %q", got, want)
}
e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
Solicited: true,
Override: true,
IsRouter: false,
})
if got, want := e.neigh.State, Reachable; got != want {
t.Errorf("got e.neigh.State = %q, want = %q", got, want)
}
if got, want := e.neigh.LinkAddr, entryTestLinkAddr2; got != want {
t.Errorf("got e.neigh.LinkAddr = %q, want = %q", got, want)
}
e.mu.Unlock()
clock.advance(c.BaseReachableTime)
wantEvents := []testEntryEventInfo{
{
EventType: entryTestAdded,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
State: Stale,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
State: Delay,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
State: Probe,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr2,
State: Reachable,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr2,
State: Stale,
},
}
nudDisp.mu.Lock()
if diff := cmp.Diff(nudDisp.events, wantEvents, eventDiffOpts()...); diff != "" {
t.Errorf("nud dispatcher events mismatch (-got, +want):\n%s", diff)
}
nudDisp.mu.Unlock()
}
func TestEntryProbeToReachableWhenSolicitedOverrideConfirmation(t *testing.T) {
c := DefaultNUDConfigurations()
// Eliminate random factors from ReachableTime computation so the transition
+1 -1
View File
@@ -177,7 +177,7 @@ type NUDHandler interface {
// Neighbor Solicitation for ARP or NDP, respectively). Validation of the
// probe needs to be performed before calling this function since the
// Neighbor Cache doesn't have access to view the NIC's assigned addresses.
HandleProbe(remoteAddr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress)
HandleProbe(remoteAddr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress, linkRes LinkAddressResolver)
// HandleConfirmation processes an incoming neighbor confirmation (e.g. ARP
// reply or Neighbor Advertisement for ARP or NDP, respectively).