mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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()
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user