github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/stack/neighbor_entry_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package stack
    16  
    17  import (
    18  	"fmt"
    19  	"math"
    20  	"math/rand"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/SagerNet/gvisor/pkg/tcpip"
    27  	"github.com/SagerNet/gvisor/pkg/tcpip/faketime"
    28  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    29  	"github.com/SagerNet/gvisor/pkg/tcpip/testutil"
    30  )
    31  
    32  const (
    33  	entryTestNetNumber tcpip.NetworkProtocolNumber = math.MaxUint32
    34  
    35  	entryTestNICID tcpip.NICID = 1
    36  
    37  	entryTestLinkAddr1 = tcpip.LinkAddress("\x0a\x00\x00\x00\x00\x01")
    38  	entryTestLinkAddr2 = tcpip.LinkAddress("\x0a\x00\x00\x00\x00\x02")
    39  )
    40  
    41  var (
    42  	entryTestAddr1 = testutil.MustParse6("a::1")
    43  	entryTestAddr2 = testutil.MustParse6("a::2")
    44  )
    45  
    46  // runImmediatelyScheduledJobs runs all jobs scheduled to run at the current
    47  // time.
    48  func runImmediatelyScheduledJobs(clock *faketime.ManualClock) {
    49  	clock.Advance(immediateDuration)
    50  }
    51  
    52  // The following unit tests exercise every state transition and verify its
    53  // behavior with RFC 4681 and RFC 7048.
    54  //
    55  // | From        | To          | Cause                                      | Update   | Action     | Event   |
    56  // | =========== | =========== | ========================================== | ======== | ===========| ======= |
    57  // | Unknown     | Unknown     | Confirmation w/ unknown address            |          |            | Added   |
    58  // | Unknown     | Incomplete  | Packet queued to unknown address           |          | Send probe | Added   |
    59  // | Unknown     | Stale       | Probe                                      |          |            | Added   |
    60  // | Incomplete  | Incomplete  | Retransmit timer expired                   |          | Send probe | Changed |
    61  // | Incomplete  | Reachable   | Solicited confirmation                     | LinkAddr | Notify     | Changed |
    62  // | Incomplete  | Stale       | Unsolicited confirmation                   | LinkAddr | Notify     | Changed |
    63  // | Incomplete  | Stale       | Probe                                      | LinkAddr | Notify     | Changed |
    64  // | Incomplete  | Unreachable | Max probes sent without reply              |          | Notify     | Changed |
    65  // | Reachable   | Reachable   | Confirmation w/ different isRouter flag    | IsRouter |            |         |
    66  // | Reachable   | Stale       | Reachable timer expired                    |          |            | Changed |
    67  // | Reachable   | Stale       | Probe or confirmation w/ different address |          |            | Changed |
    68  // | Stale       | Reachable   | Solicited override confirmation            | LinkAddr |            | Changed |
    69  // | Stale       | Reachable   | Solicited confirmation w/o address         |          | Notify     | Changed |
    70  // | Stale       | Stale       | Override confirmation                      | LinkAddr |            | Changed |
    71  // | Stale       | Stale       | Probe w/ different address                 | LinkAddr |            | Changed |
    72  // | Stale       | Delay       | Packet sent                                |          |            | Changed |
    73  // | Delay       | Reachable   | Upper-layer confirmation                   |          |            | Changed |
    74  // | Delay       | Reachable   | Solicited override confirmation            | LinkAddr |            | Changed |
    75  // | Delay       | Reachable   | Solicited confirmation w/o address         |          | Notify     | Changed |
    76  // | Delay       | Stale       | Probe or confirmation w/ different address |          |            | Changed |
    77  // | Delay       | Probe       | Delay timer expired                        |          | Send probe | Changed |
    78  // | Probe       | Reachable   | Solicited override confirmation            | LinkAddr |            | Changed |
    79  // | Probe       | Reachable   | Solicited confirmation w/ same address     |          | Notify     | Changed |
    80  // | Probe       | Reachable   | Solicited confirmation w/o address         |          | Notify     | Changed |
    81  // | Probe       | Stale       | Probe or confirmation w/ different address |          |            | Changed |
    82  // | Probe       | Probe       | Retransmit timer expired                   |          |            | Changed |
    83  // | Probe       | Unreachable | Max probes sent without reply              |          | Notify     | Changed |
    84  // | Unreachable | Incomplete  | Packet queued                              |          | Send probe | Changed |
    85  // | Unreachable | Stale       | Probe w/ different address                 | LinkAddr |            | Changed |
    86  
    87  type testEntryEventType uint8
    88  
    89  const (
    90  	entryTestAdded testEntryEventType = iota
    91  	entryTestChanged
    92  	entryTestRemoved
    93  )
    94  
    95  func (t testEntryEventType) String() string {
    96  	switch t {
    97  	case entryTestAdded:
    98  		return "add"
    99  	case entryTestChanged:
   100  		return "change"
   101  	case entryTestRemoved:
   102  		return "remove"
   103  	default:
   104  		return fmt.Sprintf("unknown (%d)", t)
   105  	}
   106  }
   107  
   108  // Fields are exported for use with cmp.Diff.
   109  type testEntryEventInfo struct {
   110  	EventType testEntryEventType
   111  	NICID     tcpip.NICID
   112  	Entry     NeighborEntry
   113  }
   114  
   115  func (e testEntryEventInfo) String() string {
   116  	return fmt.Sprintf("%s event for NIC #%d, %#v", e.EventType, e.NICID, e.Entry)
   117  }
   118  
   119  // testNUDDispatcher implements NUDDispatcher to validate the dispatching of
   120  // events upon certain NUD state machine events.
   121  type testNUDDispatcher struct {
   122  	mu struct {
   123  		sync.Mutex
   124  		events []testEntryEventInfo
   125  	}
   126  }
   127  
   128  var _ NUDDispatcher = (*testNUDDispatcher)(nil)
   129  
   130  func (d *testNUDDispatcher) queueEvent(e testEntryEventInfo) {
   131  	d.mu.Lock()
   132  	defer d.mu.Unlock()
   133  	d.mu.events = append(d.mu.events, e)
   134  }
   135  
   136  func (d *testNUDDispatcher) OnNeighborAdded(nicID tcpip.NICID, entry NeighborEntry) {
   137  	d.queueEvent(testEntryEventInfo{
   138  		EventType: entryTestAdded,
   139  		NICID:     nicID,
   140  		Entry:     entry,
   141  	})
   142  }
   143  
   144  func (d *testNUDDispatcher) OnNeighborChanged(nicID tcpip.NICID, entry NeighborEntry) {
   145  	d.queueEvent(testEntryEventInfo{
   146  		EventType: entryTestChanged,
   147  		NICID:     nicID,
   148  		Entry:     entry,
   149  	})
   150  }
   151  
   152  func (d *testNUDDispatcher) OnNeighborRemoved(nicID tcpip.NICID, entry NeighborEntry) {
   153  	d.queueEvent(testEntryEventInfo{
   154  		EventType: entryTestRemoved,
   155  		NICID:     nicID,
   156  		Entry:     entry,
   157  	})
   158  }
   159  
   160  type entryTestLinkResolver struct {
   161  	mu struct {
   162  		sync.Mutex
   163  		probes []entryTestProbeInfo
   164  	}
   165  }
   166  
   167  var _ LinkAddressResolver = (*entryTestLinkResolver)(nil)
   168  
   169  type entryTestProbeInfo struct {
   170  	RemoteAddress     tcpip.Address
   171  	RemoteLinkAddress tcpip.LinkAddress
   172  	LocalAddress      tcpip.Address
   173  }
   174  
   175  func (p entryTestProbeInfo) String() string {
   176  	return fmt.Sprintf("probe with RemoteAddress=%q, RemoteLinkAddress=%q, LocalAddress=%q", p.RemoteAddress, p.RemoteLinkAddress, p.LocalAddress)
   177  }
   178  
   179  // LinkAddressRequest sends a request for the LinkAddress of addr. Broadcasts
   180  // to the local network if linkAddr is the zero value.
   181  func (r *entryTestLinkResolver) LinkAddressRequest(targetAddr, localAddr tcpip.Address, linkAddr tcpip.LinkAddress) tcpip.Error {
   182  	r.mu.Lock()
   183  	defer r.mu.Unlock()
   184  	r.mu.probes = append(r.mu.probes, entryTestProbeInfo{
   185  		RemoteAddress:     targetAddr,
   186  		RemoteLinkAddress: linkAddr,
   187  		LocalAddress:      localAddr,
   188  	})
   189  	return nil
   190  }
   191  
   192  // ResolveStaticAddress attempts to resolve address without sending requests.
   193  // It either resolves the name immediately or returns the empty LinkAddress.
   194  func (*entryTestLinkResolver) ResolveStaticAddress(tcpip.Address) (tcpip.LinkAddress, bool) {
   195  	return "", false
   196  }
   197  
   198  // LinkAddressProtocol returns the network protocol of the addresses this
   199  // resolver can resolve.
   200  func (*entryTestLinkResolver) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
   201  	return entryTestNetNumber
   202  }
   203  
   204  func entryTestSetup(c NUDConfigurations) (*neighborEntry, *testNUDDispatcher, *entryTestLinkResolver, *faketime.ManualClock) {
   205  	clock := faketime.NewManualClock()
   206  	disp := testNUDDispatcher{}
   207  	nic := nic{
   208  		LinkEndpoint: nil, // entryTestLinkResolver doesn't use a LinkEndpoint
   209  
   210  		id: entryTestNICID,
   211  		stack: &Stack{
   212  			clock:           clock,
   213  			nudDisp:         &disp,
   214  			nudConfigs:      c,
   215  			randomGenerator: rand.New(rand.NewSource(time.Now().UnixNano())),
   216  		},
   217  		stats: makeNICStats(tcpip.NICStats{}.FillIn()),
   218  	}
   219  	netEP := (&testIPv6Protocol{}).NewEndpoint(&nic, nil)
   220  	nic.networkEndpoints = map[tcpip.NetworkProtocolNumber]NetworkEndpoint{
   221  		header.IPv6ProtocolNumber: netEP,
   222  	}
   223  
   224  	var linkRes entryTestLinkResolver
   225  	// Stub out the neighbor cache to verify deletion from the cache.
   226  	l := &linkResolver{
   227  		resolver: &linkRes,
   228  	}
   229  	l.neigh.init(&nic, &linkRes)
   230  
   231  	entry := newNeighborEntry(&l.neigh, entryTestAddr1 /* remoteAddr */, l.neigh.state)
   232  	l.neigh.mu.Lock()
   233  	l.neigh.mu.cache[entryTestAddr1] = entry
   234  	l.neigh.mu.Unlock()
   235  	nic.linkAddrResolvers = map[tcpip.NetworkProtocolNumber]*linkResolver{
   236  		header.IPv6ProtocolNumber: l,
   237  	}
   238  
   239  	return entry, &disp, &linkRes, clock
   240  }
   241  
   242  // TestEntryInitiallyUnknown verifies that the state of a newly created
   243  // neighborEntry is Unknown.
   244  func TestEntryInitiallyUnknown(t *testing.T) {
   245  	c := DefaultNUDConfigurations()
   246  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   247  
   248  	e.mu.Lock()
   249  	if e.mu.neigh.State != Unknown {
   250  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unknown)
   251  	}
   252  	e.mu.Unlock()
   253  
   254  	clock.Advance(c.RetransmitTimer)
   255  
   256  	// No probes should have been sent.
   257  	linkRes.mu.Lock()
   258  	diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   259  	linkRes.mu.Unlock()
   260  	if diff != "" {
   261  		t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   262  	}
   263  
   264  	// No events should have been dispatched.
   265  	nudDisp.mu.Lock()
   266  	if diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events); diff != "" {
   267  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   268  	}
   269  	nudDisp.mu.Unlock()
   270  }
   271  
   272  func TestEntryUnknownToUnknownWhenConfirmationWithUnknownAddress(t *testing.T) {
   273  	c := DefaultNUDConfigurations()
   274  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   275  
   276  	e.mu.Lock()
   277  	e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
   278  		Solicited: false,
   279  		Override:  false,
   280  		IsRouter:  false,
   281  	})
   282  	if e.mu.neigh.State != Unknown {
   283  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unknown)
   284  	}
   285  	e.mu.Unlock()
   286  
   287  	clock.Advance(time.Hour)
   288  
   289  	// No probes should have been sent.
   290  	linkRes.mu.Lock()
   291  	diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   292  	linkRes.mu.Unlock()
   293  	if diff != "" {
   294  		t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   295  	}
   296  
   297  	// No events should have been dispatched.
   298  	nudDisp.mu.Lock()
   299  	if diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events); diff != "" {
   300  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   301  	}
   302  	nudDisp.mu.Unlock()
   303  }
   304  
   305  func TestEntryUnknownToIncomplete(t *testing.T) {
   306  	c := DefaultNUDConfigurations()
   307  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   308  
   309  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   310  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   311  	}
   312  }
   313  
   314  func unknownToIncomplete(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
   315  	if err := func() error {
   316  		e.mu.Lock()
   317  		defer e.mu.Unlock()
   318  
   319  		if e.mu.neigh.State != Unknown {
   320  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unknown)
   321  		}
   322  		e.handlePacketQueuedLocked(entryTestAddr2)
   323  		if e.mu.neigh.State != Incomplete {
   324  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete)
   325  		}
   326  		return nil
   327  	}(); err != nil {
   328  		return err
   329  	}
   330  
   331  	runImmediatelyScheduledJobs(clock)
   332  	wantProbes := []entryTestProbeInfo{
   333  		{
   334  			RemoteAddress:     entryTestAddr1,
   335  			RemoteLinkAddress: tcpip.LinkAddress(""),
   336  			LocalAddress:      entryTestAddr2,
   337  		},
   338  	}
   339  	linkRes.mu.Lock()
   340  	diff := cmp.Diff(wantProbes, linkRes.mu.probes)
   341  	linkRes.mu.probes = nil
   342  	linkRes.mu.Unlock()
   343  	if diff != "" {
   344  		return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   345  	}
   346  
   347  	wantEvents := []testEntryEventInfo{
   348  		{
   349  			EventType: entryTestAdded,
   350  			NICID:     entryTestNICID,
   351  			Entry: NeighborEntry{
   352  				Addr:      entryTestAddr1,
   353  				LinkAddr:  tcpip.LinkAddress(""),
   354  				State:     Incomplete,
   355  				UpdatedAt: clock.Now(),
   356  			},
   357  		},
   358  	}
   359  	{
   360  		nudDisp.mu.Lock()
   361  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
   362  		nudDisp.mu.events = nil
   363  		nudDisp.mu.Unlock()
   364  		if diff != "" {
   365  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   366  		}
   367  	}
   368  	return nil
   369  }
   370  
   371  func TestEntryUnknownToStale(t *testing.T) {
   372  	c := DefaultNUDConfigurations()
   373  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   374  
   375  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
   376  		t.Fatalf("unknownToStale(...) = %s", err)
   377  	}
   378  }
   379  
   380  func unknownToStale(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
   381  	if err := func() error {
   382  		e.mu.Lock()
   383  		defer e.mu.Unlock()
   384  
   385  		if e.mu.neigh.State != Unknown {
   386  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unknown)
   387  		}
   388  		e.handleProbeLocked(entryTestLinkAddr1)
   389  		if e.mu.neigh.State != Stale {
   390  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
   391  		}
   392  		return nil
   393  	}(); err != nil {
   394  		return err
   395  	}
   396  
   397  	// No probes should have been sent.
   398  	runImmediatelyScheduledJobs(clock)
   399  	{
   400  		linkRes.mu.Lock()
   401  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   402  		linkRes.mu.Unlock()
   403  		if diff != "" {
   404  			return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   405  		}
   406  	}
   407  
   408  	wantEvents := []testEntryEventInfo{
   409  		{
   410  			EventType: entryTestAdded,
   411  			NICID:     entryTestNICID,
   412  			Entry: NeighborEntry{
   413  				Addr:      entryTestAddr1,
   414  				LinkAddr:  entryTestLinkAddr1,
   415  				State:     Stale,
   416  				UpdatedAt: clock.Now(),
   417  			},
   418  		},
   419  	}
   420  	{
   421  		nudDisp.mu.Lock()
   422  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
   423  		nudDisp.mu.events = nil
   424  		nudDisp.mu.Unlock()
   425  		if diff != "" {
   426  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   427  		}
   428  	}
   429  
   430  	return nil
   431  }
   432  
   433  func TestEntryIncompleteToIncompleteDoesNotChangeUpdatedAt(t *testing.T) {
   434  	c := DefaultNUDConfigurations()
   435  	c.MaxMulticastProbes = 3
   436  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   437  
   438  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   439  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   440  	}
   441  
   442  	// UpdatedAt should remain the same during address resolution.
   443  	e.mu.Lock()
   444  	startedAt := e.mu.neigh.UpdatedAt
   445  	e.mu.Unlock()
   446  
   447  	// Wait for the rest of the reachability probe transmissions, signifying
   448  	// Incomplete to Incomplete transitions.
   449  	for i := uint32(1); i < c.MaxMulticastProbes; i++ {
   450  		clock.Advance(c.RetransmitTimer)
   451  
   452  		wantProbes := []entryTestProbeInfo{
   453  			{
   454  				RemoteAddress:     entryTestAddr1,
   455  				RemoteLinkAddress: tcpip.LinkAddress(""),
   456  				LocalAddress:      entryTestAddr2,
   457  			},
   458  		}
   459  		linkRes.mu.Lock()
   460  		diff := cmp.Diff(wantProbes, linkRes.mu.probes)
   461  		linkRes.mu.probes = nil
   462  		linkRes.mu.Unlock()
   463  		if diff != "" {
   464  			t.Fatalf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   465  		}
   466  
   467  		e.mu.Lock()
   468  		if got, want := e.mu.neigh.UpdatedAt, startedAt; got != want {
   469  			t.Errorf("got e.mu.neigh.UpdatedAt = %q, want = %q", got, want)
   470  		}
   471  		e.mu.Unlock()
   472  	}
   473  
   474  	// UpdatedAt should change after failing address resolution. Timing out after
   475  	// sending the last probe transitions the entry to Unreachable.
   476  	clock.Advance(c.RetransmitTimer)
   477  
   478  	wantEvents := []testEntryEventInfo{
   479  		{
   480  			EventType: entryTestChanged,
   481  			NICID:     entryTestNICID,
   482  			Entry: NeighborEntry{
   483  				Addr:      entryTestAddr1,
   484  				LinkAddr:  tcpip.LinkAddress(""),
   485  				State:     Unreachable,
   486  				UpdatedAt: clock.Now(),
   487  			},
   488  		},
   489  	}
   490  	nudDisp.mu.Lock()
   491  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
   492  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   493  	}
   494  	nudDisp.mu.Unlock()
   495  }
   496  
   497  func TestEntryIncompleteToReachable(t *testing.T) {
   498  	c := DefaultNUDConfigurations()
   499  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   500  
   501  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   502  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   503  	}
   504  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
   505  		t.Fatalf("incompleteToReachable(...) = %s", err)
   506  	}
   507  }
   508  
   509  func incompleteToReachableWithFlags(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock, flags ReachabilityConfirmationFlags) error {
   510  	if err := func() error {
   511  		e.mu.Lock()
   512  		defer e.mu.Unlock()
   513  
   514  		if e.mu.neigh.State != Incomplete {
   515  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete)
   516  		}
   517  		e.handleConfirmationLocked(entryTestLinkAddr1, flags)
   518  		if e.mu.neigh.State != Reachable {
   519  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
   520  		}
   521  		if e.mu.neigh.LinkAddr != entryTestLinkAddr1 {
   522  			return fmt.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1)
   523  		}
   524  		return nil
   525  	}(); err != nil {
   526  		return err
   527  	}
   528  
   529  	// No probes should have been sent.
   530  	runImmediatelyScheduledJobs(clock)
   531  	{
   532  		linkRes.mu.Lock()
   533  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   534  		linkRes.mu.Unlock()
   535  		if diff != "" {
   536  			return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   537  		}
   538  	}
   539  
   540  	wantEvents := []testEntryEventInfo{
   541  		{
   542  			EventType: entryTestChanged,
   543  			NICID:     entryTestNICID,
   544  			Entry: NeighborEntry{
   545  				Addr:      entryTestAddr1,
   546  				LinkAddr:  entryTestLinkAddr1,
   547  				State:     Reachable,
   548  				UpdatedAt: clock.Now(),
   549  			},
   550  		},
   551  	}
   552  	{
   553  		nudDisp.mu.Lock()
   554  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
   555  		nudDisp.mu.events = nil
   556  		nudDisp.mu.Unlock()
   557  		if diff != "" {
   558  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   559  		}
   560  	}
   561  
   562  	return nil
   563  }
   564  
   565  func incompleteToReachable(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
   566  	if err := incompleteToReachableWithFlags(e, nudDisp, linkRes, clock, ReachabilityConfirmationFlags{
   567  		Solicited: true,
   568  		Override:  false,
   569  		IsRouter:  false,
   570  	}); err != nil {
   571  		return err
   572  	}
   573  
   574  	e.mu.Lock()
   575  	isRouter := e.mu.isRouter
   576  	e.mu.Unlock()
   577  	if isRouter {
   578  		return fmt.Errorf("got e.mu.isRouter = %t, want = false", isRouter)
   579  	}
   580  
   581  	return nil
   582  }
   583  
   584  func TestEntryIncompleteToReachableWithRouterFlag(t *testing.T) {
   585  	c := DefaultNUDConfigurations()
   586  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   587  
   588  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   589  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   590  	}
   591  	if err := incompleteToReachableWithRouterFlag(e, nudDisp, linkRes, clock); err != nil {
   592  		t.Fatalf("incompleteToReachableWithRouterFlag(...) = %s", err)
   593  	}
   594  }
   595  
   596  func incompleteToReachableWithRouterFlag(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
   597  	if err := incompleteToReachableWithFlags(e, nudDisp, linkRes, clock, ReachabilityConfirmationFlags{
   598  		Solicited: true,
   599  		Override:  false,
   600  		IsRouter:  true,
   601  	}); err != nil {
   602  		return err
   603  	}
   604  
   605  	e.mu.Lock()
   606  	isRouter := e.mu.isRouter
   607  	e.mu.Unlock()
   608  	if !isRouter {
   609  		return fmt.Errorf("got e.mu.isRouter = %t, want = true", isRouter)
   610  	}
   611  
   612  	return nil
   613  }
   614  
   615  func TestEntryIncompleteToStaleWhenUnsolicitedConfirmation(t *testing.T) {
   616  	c := DefaultNUDConfigurations()
   617  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   618  
   619  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   620  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   621  	}
   622  
   623  	e.mu.Lock()
   624  	e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
   625  		Solicited: false,
   626  		Override:  false,
   627  		IsRouter:  false,
   628  	})
   629  	if e.mu.neigh.State != Stale {
   630  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
   631  	}
   632  	if e.mu.isRouter {
   633  		t.Errorf("got e.mu.isRouter = %t, want = false", e.mu.isRouter)
   634  	}
   635  	e.mu.Unlock()
   636  
   637  	wantEvents := []testEntryEventInfo{
   638  		{
   639  			EventType: entryTestChanged,
   640  			NICID:     entryTestNICID,
   641  			Entry: NeighborEntry{
   642  				Addr:      entryTestAddr1,
   643  				LinkAddr:  entryTestLinkAddr1,
   644  				State:     Stale,
   645  				UpdatedAt: clock.Now(),
   646  			},
   647  		},
   648  	}
   649  	nudDisp.mu.Lock()
   650  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
   651  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   652  	}
   653  	nudDisp.mu.Unlock()
   654  }
   655  
   656  func TestEntryIncompleteToStaleWhenProbe(t *testing.T) {
   657  	c := DefaultNUDConfigurations()
   658  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   659  
   660  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   661  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   662  	}
   663  
   664  	e.mu.Lock()
   665  	e.handleProbeLocked(entryTestLinkAddr1)
   666  	if e.mu.neigh.State != Stale {
   667  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
   668  	}
   669  	e.mu.Unlock()
   670  
   671  	wantEvents := []testEntryEventInfo{
   672  		{
   673  			EventType: entryTestChanged,
   674  			NICID:     entryTestNICID,
   675  			Entry: NeighborEntry{
   676  				Addr:      entryTestAddr1,
   677  				LinkAddr:  entryTestLinkAddr1,
   678  				State:     Stale,
   679  				UpdatedAt: clock.Now(),
   680  			},
   681  		},
   682  	}
   683  	nudDisp.mu.Lock()
   684  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
   685  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   686  	}
   687  	nudDisp.mu.Unlock()
   688  }
   689  
   690  func TestEntryIncompleteToUnreachable(t *testing.T) {
   691  	c := DefaultNUDConfigurations()
   692  	c.MaxMulticastProbes = 3
   693  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   694  
   695  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   696  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   697  	}
   698  	if err := incompleteToUnreachable(c, e, nudDisp, linkRes, clock); err != nil {
   699  		t.Fatalf("incompleteToUnreachable(...) = %s", err)
   700  	}
   701  }
   702  
   703  func incompleteToUnreachable(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
   704  	{
   705  		e.mu.Lock()
   706  		state := e.mu.neigh.State
   707  		e.mu.Unlock()
   708  		if state != Incomplete {
   709  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Incomplete)
   710  		}
   711  	}
   712  
   713  	// The first probe was sent in the transition from Unknown to Incomplete.
   714  	clock.Advance(c.RetransmitTimer)
   715  
   716  	// Observe each subsequent multicast probe transmitted.
   717  	for i := uint32(1); i < c.MaxMulticastProbes; i++ {
   718  		wantProbes := []entryTestProbeInfo{{
   719  			RemoteAddress:     entryTestAddr1,
   720  			RemoteLinkAddress: "",
   721  			LocalAddress:      entryTestAddr2,
   722  		}}
   723  		linkRes.mu.Lock()
   724  		diff := cmp.Diff(wantProbes, linkRes.mu.probes)
   725  		linkRes.mu.probes = nil
   726  		linkRes.mu.Unlock()
   727  		if diff != "" {
   728  			return fmt.Errorf("link address resolver probe #%d mismatch (-want, +got):\n%s", i+1, diff)
   729  		}
   730  
   731  		e.mu.Lock()
   732  		state := e.mu.neigh.State
   733  		e.mu.Unlock()
   734  		if state != Incomplete {
   735  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Incomplete)
   736  		}
   737  
   738  		clock.Advance(c.RetransmitTimer)
   739  	}
   740  
   741  	{
   742  		e.mu.Lock()
   743  		state := e.mu.neigh.State
   744  		e.mu.Unlock()
   745  		if state != Unreachable {
   746  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Unreachable)
   747  		}
   748  	}
   749  
   750  	wantEvents := []testEntryEventInfo{
   751  		{
   752  			EventType: entryTestChanged,
   753  			NICID:     entryTestNICID,
   754  			Entry: NeighborEntry{
   755  				Addr:      entryTestAddr1,
   756  				LinkAddr:  tcpip.LinkAddress(""),
   757  				State:     Unreachable,
   758  				UpdatedAt: clock.Now(),
   759  			},
   760  		},
   761  	}
   762  	nudDisp.mu.Lock()
   763  	diff := cmp.Diff(wantEvents, nudDisp.mu.events)
   764  	nudDisp.mu.events = nil
   765  	nudDisp.mu.Unlock()
   766  	if diff != "" {
   767  		return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   768  	}
   769  
   770  	return nil
   771  }
   772  
   773  type testLocker struct{}
   774  
   775  var _ sync.Locker = (*testLocker)(nil)
   776  
   777  func (*testLocker) Lock()   {}
   778  func (*testLocker) Unlock() {}
   779  
   780  func TestEntryReachableToReachableClearsRouterWhenConfirmationWithoutRouter(t *testing.T) {
   781  	c := DefaultNUDConfigurations()
   782  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   783  
   784  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   785  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   786  	}
   787  	if err := incompleteToReachableWithRouterFlag(e, nudDisp, linkRes, clock); err != nil {
   788  		t.Fatalf("incompleteToReachableWithRouterFlag(...) = %s", err)
   789  	}
   790  
   791  	e.mu.Lock()
   792  	e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
   793  		Solicited: false,
   794  		Override:  false,
   795  		IsRouter:  false,
   796  	})
   797  	if e.mu.neigh.State != Reachable {
   798  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
   799  	}
   800  	if got, want := e.mu.isRouter, false; got != want {
   801  		t.Errorf("got e.mu.isRouter = %t, want = %t", got, want)
   802  	}
   803  	ipv6EP := e.cache.nic.networkEndpoints[header.IPv6ProtocolNumber].(*testIPv6Endpoint)
   804  	if ipv6EP.invalidatedRtr != e.mu.neigh.Addr {
   805  		t.Errorf("got ipv6EP.invalidatedRtr = %s, want = %s", ipv6EP.invalidatedRtr, e.mu.neigh.Addr)
   806  	}
   807  	e.mu.Unlock()
   808  
   809  	// No probes should have been sent.
   810  	runImmediatelyScheduledJobs(clock)
   811  	{
   812  		linkRes.mu.Lock()
   813  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   814  		linkRes.mu.Unlock()
   815  		if diff != "" {
   816  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   817  		}
   818  	}
   819  
   820  	// No events should have been dispatched.
   821  	nudDisp.mu.Lock()
   822  	diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events)
   823  	nudDisp.mu.Unlock()
   824  	if diff != "" {
   825  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   826  	}
   827  }
   828  
   829  func TestEntryReachableToReachableWhenProbeWithSameAddress(t *testing.T) {
   830  	c := DefaultNUDConfigurations()
   831  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   832  
   833  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   834  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   835  	}
   836  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
   837  		t.Fatalf("incompleteToReachable(...) = %s", err)
   838  	}
   839  
   840  	e.mu.Lock()
   841  	e.handleProbeLocked(entryTestLinkAddr1)
   842  	if e.mu.neigh.State != Reachable {
   843  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
   844  	}
   845  	if e.mu.neigh.LinkAddr != entryTestLinkAddr1 {
   846  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1)
   847  	}
   848  	e.mu.Unlock()
   849  
   850  	// No probes should have been sent.
   851  	runImmediatelyScheduledJobs(clock)
   852  	{
   853  		linkRes.mu.Lock()
   854  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   855  		linkRes.mu.Unlock()
   856  		if diff != "" {
   857  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   858  		}
   859  	}
   860  
   861  	// No events should have been dispatched.
   862  	nudDisp.mu.Lock()
   863  	diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events)
   864  	nudDisp.mu.Unlock()
   865  	if diff != "" {
   866  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   867  	}
   868  }
   869  
   870  func TestEntryReachableToStaleWhenTimeout(t *testing.T) {
   871  	c := DefaultNUDConfigurations()
   872  	// Eliminate random factors from ReachableTime computation so the transition
   873  	// from Stale to Reachable will only take BaseReachableTime duration.
   874  	c.MinRandomFactor = 1
   875  	c.MaxRandomFactor = 1
   876  
   877  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   878  
   879  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   880  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   881  	}
   882  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
   883  		t.Fatalf("incompleteToReachable(...) = %s", err)
   884  	}
   885  	if err := reachableToStale(c, e, nudDisp, linkRes, clock); err != nil {
   886  		t.Fatalf("reachableToStale(...) = %s", err)
   887  	}
   888  }
   889  
   890  // reachableToStale transitions a neighborEntry in Reachable state to Stale
   891  // state. Depends on the elimination of random factors in the ReachableTime
   892  // computation.
   893  //
   894  //	c.MinRandomFactor = 1
   895  //	c.MaxRandomFactor = 1
   896  func reachableToStale(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
   897  	// Ensure there are no random factors in the ReachableTime computation.
   898  	if c.MinRandomFactor != 1 {
   899  		return fmt.Errorf("got c.MinRandomFactor = %f, want = 1", c.MinRandomFactor)
   900  	}
   901  	if c.MaxRandomFactor != 1 {
   902  		return fmt.Errorf("got c.MaxRandomFactor = %f, want = 1", c.MaxRandomFactor)
   903  	}
   904  
   905  	{
   906  		e.mu.Lock()
   907  		state := e.mu.neigh.State
   908  		e.mu.Unlock()
   909  		if state != Reachable {
   910  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Reachable)
   911  		}
   912  	}
   913  
   914  	clock.Advance(c.BaseReachableTime)
   915  
   916  	{
   917  		e.mu.Lock()
   918  		state := e.mu.neigh.State
   919  		e.mu.Unlock()
   920  		if state != Stale {
   921  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Stale)
   922  		}
   923  	}
   924  
   925  	// No probes should have been sent.
   926  	runImmediatelyScheduledJobs(clock)
   927  	{
   928  		linkRes.mu.Lock()
   929  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   930  		linkRes.mu.Unlock()
   931  		if diff != "" {
   932  			return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   933  		}
   934  	}
   935  
   936  	wantEvents := []testEntryEventInfo{
   937  		{
   938  			EventType: entryTestChanged,
   939  			NICID:     entryTestNICID,
   940  			Entry: NeighborEntry{
   941  				Addr:      entryTestAddr1,
   942  				LinkAddr:  entryTestLinkAddr1,
   943  				State:     Stale,
   944  				UpdatedAt: clock.Now(),
   945  			},
   946  		},
   947  	}
   948  	{
   949  
   950  		nudDisp.mu.Lock()
   951  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
   952  		nudDisp.mu.events = nil
   953  		nudDisp.mu.Unlock()
   954  		if diff != "" {
   955  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
   956  		}
   957  	}
   958  
   959  	return nil
   960  }
   961  
   962  func TestEntryReachableToStaleWhenProbeWithDifferentAddress(t *testing.T) {
   963  	c := DefaultNUDConfigurations()
   964  	e, nudDisp, linkRes, clock := entryTestSetup(c)
   965  
   966  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
   967  		t.Fatalf("unknownToIncomplete(...) = %s", err)
   968  	}
   969  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
   970  		t.Fatalf("incompleteToReachable(...) = %s", err)
   971  	}
   972  
   973  	e.mu.Lock()
   974  	e.handleProbeLocked(entryTestLinkAddr2)
   975  	if e.mu.neigh.State != Stale {
   976  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
   977  	}
   978  	e.mu.Unlock()
   979  
   980  	// No probes should have been sent.
   981  	runImmediatelyScheduledJobs(clock)
   982  	{
   983  		linkRes.mu.Lock()
   984  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
   985  		linkRes.mu.Unlock()
   986  		if diff != "" {
   987  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
   988  		}
   989  	}
   990  
   991  	wantEvents := []testEntryEventInfo{
   992  		{
   993  			EventType: entryTestChanged,
   994  			NICID:     entryTestNICID,
   995  			Entry: NeighborEntry{
   996  				Addr:      entryTestAddr1,
   997  				LinkAddr:  entryTestLinkAddr2,
   998  				State:     Stale,
   999  				UpdatedAt: clock.Now(),
  1000  			},
  1001  		},
  1002  	}
  1003  	nudDisp.mu.Lock()
  1004  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1005  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1006  	}
  1007  	nudDisp.mu.Unlock()
  1008  }
  1009  
  1010  func TestEntryReachableToStaleWhenConfirmationWithDifferentAddress(t *testing.T) {
  1011  	c := DefaultNUDConfigurations()
  1012  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1013  
  1014  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
  1015  		t.Fatalf("unknownToIncomplete(...) = %s", err)
  1016  	}
  1017  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
  1018  		t.Fatalf("incompleteToReachable(...) = %s", err)
  1019  	}
  1020  
  1021  	e.mu.Lock()
  1022  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1023  		Solicited: false,
  1024  		Override:  false,
  1025  		IsRouter:  false,
  1026  	})
  1027  	if e.mu.neigh.State != Stale {
  1028  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1029  	}
  1030  	e.mu.Unlock()
  1031  
  1032  	// No probes should have been sent.
  1033  	runImmediatelyScheduledJobs(clock)
  1034  	{
  1035  		linkRes.mu.Lock()
  1036  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1037  		linkRes.mu.Unlock()
  1038  		if diff != "" {
  1039  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1040  		}
  1041  	}
  1042  
  1043  	wantEvents := []testEntryEventInfo{
  1044  		{
  1045  			EventType: entryTestChanged,
  1046  			NICID:     entryTestNICID,
  1047  			Entry: NeighborEntry{
  1048  				Addr:      entryTestAddr1,
  1049  				LinkAddr:  entryTestLinkAddr1,
  1050  				State:     Stale,
  1051  				UpdatedAt: clock.Now(),
  1052  			},
  1053  		},
  1054  	}
  1055  	nudDisp.mu.Lock()
  1056  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1057  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1058  	}
  1059  	nudDisp.mu.Unlock()
  1060  }
  1061  
  1062  func TestEntryReachableToStaleWhenConfirmationWithDifferentAddressAndOverride(t *testing.T) {
  1063  	c := DefaultNUDConfigurations()
  1064  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1065  
  1066  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
  1067  		t.Fatalf("unknownToIncomplete(...) = %s", err)
  1068  	}
  1069  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
  1070  		t.Fatalf("incompleteToReachable(...) = %s", err)
  1071  	}
  1072  
  1073  	e.mu.Lock()
  1074  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1075  		Solicited: false,
  1076  		Override:  true,
  1077  		IsRouter:  false,
  1078  	})
  1079  	if e.mu.neigh.State != Stale {
  1080  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1081  	}
  1082  	e.mu.Unlock()
  1083  
  1084  	// No probes should have been sent.
  1085  	runImmediatelyScheduledJobs(clock)
  1086  	{
  1087  		linkRes.mu.Lock()
  1088  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1089  		linkRes.mu.Unlock()
  1090  		if diff != "" {
  1091  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1092  		}
  1093  	}
  1094  
  1095  	wantEvents := []testEntryEventInfo{
  1096  		{
  1097  			EventType: entryTestChanged,
  1098  			NICID:     entryTestNICID,
  1099  			Entry: NeighborEntry{
  1100  				Addr:      entryTestAddr1,
  1101  				LinkAddr:  entryTestLinkAddr2,
  1102  				State:     Stale,
  1103  				UpdatedAt: clock.Now(),
  1104  			},
  1105  		},
  1106  	}
  1107  	nudDisp.mu.Lock()
  1108  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1109  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1110  	}
  1111  	nudDisp.mu.Unlock()
  1112  }
  1113  
  1114  func TestEntryStaleToStaleWhenProbeWithSameAddress(t *testing.T) {
  1115  	c := DefaultNUDConfigurations()
  1116  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1117  
  1118  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1119  		t.Fatalf("unknownToStale(...) = %s", err)
  1120  	}
  1121  
  1122  	e.mu.Lock()
  1123  	e.handleProbeLocked(entryTestLinkAddr1)
  1124  	if e.mu.neigh.State != Stale {
  1125  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1126  	}
  1127  	if e.mu.neigh.LinkAddr != entryTestLinkAddr1 {
  1128  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1)
  1129  	}
  1130  	e.mu.Unlock()
  1131  
  1132  	// No probes should have been sent.
  1133  	runImmediatelyScheduledJobs(clock)
  1134  	{
  1135  		linkRes.mu.Lock()
  1136  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1137  		linkRes.mu.Unlock()
  1138  		if diff != "" {
  1139  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1140  		}
  1141  	}
  1142  
  1143  	// No events should have been dispatched.
  1144  	nudDisp.mu.Lock()
  1145  	if diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events); diff != "" {
  1146  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1147  	}
  1148  	nudDisp.mu.Unlock()
  1149  }
  1150  
  1151  func TestEntryStaleToReachableWhenSolicitedOverrideConfirmation(t *testing.T) {
  1152  	c := DefaultNUDConfigurations()
  1153  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1154  
  1155  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1156  		t.Fatalf("unknownToStale(...) = %s", err)
  1157  	}
  1158  
  1159  	e.mu.Lock()
  1160  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1161  		Solicited: true,
  1162  		Override:  true,
  1163  		IsRouter:  false,
  1164  	})
  1165  	if e.mu.neigh.State != Reachable {
  1166  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
  1167  	}
  1168  	if e.mu.neigh.LinkAddr != entryTestLinkAddr2 {
  1169  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr2)
  1170  	}
  1171  	e.mu.Unlock()
  1172  
  1173  	// No probes should have been sent.
  1174  	runImmediatelyScheduledJobs(clock)
  1175  	{
  1176  		linkRes.mu.Lock()
  1177  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1178  		linkRes.mu.Unlock()
  1179  		if diff != "" {
  1180  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1181  		}
  1182  	}
  1183  
  1184  	wantEvents := []testEntryEventInfo{
  1185  		{
  1186  			EventType: entryTestChanged,
  1187  			NICID:     entryTestNICID,
  1188  			Entry: NeighborEntry{
  1189  				Addr:      entryTestAddr1,
  1190  				LinkAddr:  entryTestLinkAddr2,
  1191  				State:     Reachable,
  1192  				UpdatedAt: clock.Now(),
  1193  			},
  1194  		},
  1195  	}
  1196  	nudDisp.mu.Lock()
  1197  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1198  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1199  	}
  1200  	nudDisp.mu.Unlock()
  1201  }
  1202  
  1203  func TestEntryStaleToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) {
  1204  	c := DefaultNUDConfigurations()
  1205  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1206  
  1207  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1208  		t.Fatalf("unknownToStale(...) = %s", err)
  1209  	}
  1210  
  1211  	e.mu.Lock()
  1212  	e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{
  1213  		Solicited: true,
  1214  		Override:  false,
  1215  		IsRouter:  false,
  1216  	})
  1217  	if e.mu.neigh.State != Reachable {
  1218  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
  1219  	}
  1220  	if e.mu.neigh.LinkAddr != entryTestLinkAddr1 {
  1221  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1)
  1222  	}
  1223  	e.mu.Unlock()
  1224  
  1225  	// No probes should have been sent.
  1226  	runImmediatelyScheduledJobs(clock)
  1227  	{
  1228  		linkRes.mu.Lock()
  1229  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1230  		linkRes.mu.Unlock()
  1231  		if diff != "" {
  1232  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1233  		}
  1234  	}
  1235  
  1236  	wantEvents := []testEntryEventInfo{
  1237  		{
  1238  			EventType: entryTestChanged,
  1239  			NICID:     entryTestNICID,
  1240  			Entry: NeighborEntry{
  1241  				Addr:      entryTestAddr1,
  1242  				LinkAddr:  entryTestLinkAddr1,
  1243  				State:     Reachable,
  1244  				UpdatedAt: clock.Now(),
  1245  			},
  1246  		},
  1247  	}
  1248  	nudDisp.mu.Lock()
  1249  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1250  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1251  	}
  1252  	nudDisp.mu.Unlock()
  1253  }
  1254  
  1255  func TestEntryStaleToStaleWhenOverrideConfirmation(t *testing.T) {
  1256  	c := DefaultNUDConfigurations()
  1257  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1258  
  1259  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1260  		t.Fatalf("unknownToStale(...) = %s", err)
  1261  	}
  1262  
  1263  	e.mu.Lock()
  1264  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1265  		Solicited: false,
  1266  		Override:  true,
  1267  		IsRouter:  false,
  1268  	})
  1269  	if e.mu.neigh.State != Stale {
  1270  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1271  	}
  1272  	if e.mu.neigh.LinkAddr != entryTestLinkAddr2 {
  1273  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr2)
  1274  	}
  1275  	e.mu.Unlock()
  1276  
  1277  	wantEvents := []testEntryEventInfo{
  1278  		{
  1279  			EventType: entryTestChanged,
  1280  			NICID:     entryTestNICID,
  1281  			Entry: NeighborEntry{
  1282  				Addr:      entryTestAddr1,
  1283  				LinkAddr:  entryTestLinkAddr2,
  1284  				State:     Stale,
  1285  				UpdatedAt: clock.Now(),
  1286  			},
  1287  		},
  1288  	}
  1289  	nudDisp.mu.Lock()
  1290  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1291  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1292  	}
  1293  	nudDisp.mu.Unlock()
  1294  }
  1295  
  1296  func TestEntryStaleToStaleWhenProbeUpdateAddress(t *testing.T) {
  1297  	c := DefaultNUDConfigurations()
  1298  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1299  
  1300  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1301  		t.Fatalf("unknownToStale(...) = %s", err)
  1302  	}
  1303  
  1304  	e.mu.Lock()
  1305  	e.handleProbeLocked(entryTestLinkAddr2)
  1306  	if e.mu.neigh.State != Stale {
  1307  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1308  	}
  1309  	if e.mu.neigh.LinkAddr != entryTestLinkAddr2 {
  1310  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr2)
  1311  	}
  1312  	e.mu.Unlock()
  1313  
  1314  	// No probes should have been sent.
  1315  	runImmediatelyScheduledJobs(clock)
  1316  	{
  1317  		linkRes.mu.Lock()
  1318  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1319  		linkRes.mu.Unlock()
  1320  		if diff != "" {
  1321  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1322  		}
  1323  	}
  1324  
  1325  	wantEvents := []testEntryEventInfo{
  1326  		{
  1327  			EventType: entryTestChanged,
  1328  			NICID:     entryTestNICID,
  1329  			Entry: NeighborEntry{
  1330  				Addr:      entryTestAddr1,
  1331  				LinkAddr:  entryTestLinkAddr2,
  1332  				State:     Stale,
  1333  				UpdatedAt: clock.Now(),
  1334  			},
  1335  		},
  1336  	}
  1337  	nudDisp.mu.Lock()
  1338  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1339  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1340  	}
  1341  	nudDisp.mu.Unlock()
  1342  }
  1343  
  1344  func TestEntryStaleToDelay(t *testing.T) {
  1345  	c := DefaultNUDConfigurations()
  1346  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1347  
  1348  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1349  		t.Fatalf("unknownToStale(...) = %s", err)
  1350  	}
  1351  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1352  		t.Fatalf("staleToDelay(...) = %s", err)
  1353  	}
  1354  }
  1355  
  1356  func staleToDelay(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
  1357  	if err := func() error {
  1358  		e.mu.Lock()
  1359  		defer e.mu.Unlock()
  1360  
  1361  		if e.mu.neigh.State != Stale {
  1362  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1363  		}
  1364  		e.handlePacketQueuedLocked(entryTestAddr2)
  1365  		if e.mu.neigh.State != Delay {
  1366  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay)
  1367  		}
  1368  		return nil
  1369  	}(); err != nil {
  1370  		return err
  1371  	}
  1372  
  1373  	// No probes should have been sent.
  1374  	runImmediatelyScheduledJobs(clock)
  1375  	{
  1376  		linkRes.mu.Lock()
  1377  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1378  		linkRes.mu.Unlock()
  1379  		if diff != "" {
  1380  			return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1381  		}
  1382  	}
  1383  
  1384  	wantEvents := []testEntryEventInfo{
  1385  		{
  1386  			EventType: entryTestChanged,
  1387  			NICID:     entryTestNICID,
  1388  			Entry: NeighborEntry{
  1389  				Addr:      entryTestAddr1,
  1390  				LinkAddr:  entryTestLinkAddr1,
  1391  				State:     Delay,
  1392  				UpdatedAt: clock.Now(),
  1393  			},
  1394  		},
  1395  	}
  1396  	nudDisp.mu.Lock()
  1397  	diff := cmp.Diff(wantEvents, nudDisp.mu.events)
  1398  	nudDisp.mu.events = nil
  1399  	nudDisp.mu.Unlock()
  1400  	if diff != "" {
  1401  		return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1402  	}
  1403  
  1404  	return nil
  1405  }
  1406  
  1407  func TestEntryDelayToReachableWhenUpperLevelConfirmation(t *testing.T) {
  1408  	c := DefaultNUDConfigurations()
  1409  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1410  
  1411  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1412  		t.Fatalf("unknownToStale(...) = %s", err)
  1413  	}
  1414  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1415  		t.Fatalf("staleToDelay(...) = %s", err)
  1416  	}
  1417  
  1418  	e.mu.Lock()
  1419  	e.handleUpperLevelConfirmationLocked()
  1420  	if e.mu.neigh.State != Reachable {
  1421  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
  1422  	}
  1423  	e.mu.Unlock()
  1424  
  1425  	// No probes should have been sent.
  1426  	runImmediatelyScheduledJobs(clock)
  1427  	{
  1428  		linkRes.mu.Lock()
  1429  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1430  		linkRes.mu.Unlock()
  1431  		if diff != "" {
  1432  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1433  		}
  1434  	}
  1435  
  1436  	wantEvents := []testEntryEventInfo{
  1437  		{
  1438  			EventType: entryTestChanged,
  1439  			NICID:     entryTestNICID,
  1440  			Entry: NeighborEntry{
  1441  				Addr:      entryTestAddr1,
  1442  				LinkAddr:  entryTestLinkAddr1,
  1443  				State:     Reachable,
  1444  				UpdatedAt: clock.Now(),
  1445  			},
  1446  		},
  1447  	}
  1448  	nudDisp.mu.Lock()
  1449  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1450  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1451  	}
  1452  	nudDisp.mu.Unlock()
  1453  }
  1454  
  1455  func TestEntryDelayToReachableWhenSolicitedOverrideConfirmation(t *testing.T) {
  1456  	c := DefaultNUDConfigurations()
  1457  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1458  
  1459  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1460  		t.Fatalf("unknownToStale(...) = %s", err)
  1461  	}
  1462  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1463  		t.Fatalf("staleToDelay(...) = %s", err)
  1464  	}
  1465  
  1466  	e.mu.Lock()
  1467  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1468  		Solicited: true,
  1469  		Override:  true,
  1470  		IsRouter:  false,
  1471  	})
  1472  	if e.mu.neigh.State != Reachable {
  1473  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
  1474  	}
  1475  	if e.mu.neigh.LinkAddr != entryTestLinkAddr2 {
  1476  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr2)
  1477  	}
  1478  	e.mu.Unlock()
  1479  
  1480  	// No probes should have been sent.
  1481  	runImmediatelyScheduledJobs(clock)
  1482  	{
  1483  		linkRes.mu.Lock()
  1484  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1485  		linkRes.mu.Unlock()
  1486  		if diff != "" {
  1487  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1488  		}
  1489  	}
  1490  
  1491  	wantEvents := []testEntryEventInfo{
  1492  		{
  1493  			EventType: entryTestChanged,
  1494  			NICID:     entryTestNICID,
  1495  			Entry: NeighborEntry{
  1496  				Addr:      entryTestAddr1,
  1497  				LinkAddr:  entryTestLinkAddr2,
  1498  				State:     Reachable,
  1499  				UpdatedAt: clock.Now(),
  1500  			},
  1501  		},
  1502  	}
  1503  	nudDisp.mu.Lock()
  1504  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1505  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1506  	}
  1507  	nudDisp.mu.Unlock()
  1508  }
  1509  
  1510  func TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) {
  1511  	c := DefaultNUDConfigurations()
  1512  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1513  
  1514  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1515  		t.Fatalf("unknownToStale(...) = %s", err)
  1516  	}
  1517  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1518  		t.Fatalf("staleToDelay(...) = %s", err)
  1519  	}
  1520  
  1521  	e.mu.Lock()
  1522  	e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{
  1523  		Solicited: true,
  1524  		Override:  false,
  1525  		IsRouter:  false,
  1526  	})
  1527  	if e.mu.neigh.State != Reachable {
  1528  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
  1529  	}
  1530  	if e.mu.neigh.LinkAddr != entryTestLinkAddr1 {
  1531  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1)
  1532  	}
  1533  	e.mu.Unlock()
  1534  
  1535  	// No probes should have been sent.
  1536  	runImmediatelyScheduledJobs(clock)
  1537  	{
  1538  		linkRes.mu.Lock()
  1539  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1540  		linkRes.mu.Unlock()
  1541  		if diff != "" {
  1542  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1543  		}
  1544  	}
  1545  
  1546  	wantEvents := []testEntryEventInfo{
  1547  		{
  1548  			EventType: entryTestChanged,
  1549  			NICID:     entryTestNICID,
  1550  			Entry: NeighborEntry{
  1551  				Addr:      entryTestAddr1,
  1552  				LinkAddr:  entryTestLinkAddr1,
  1553  				State:     Reachable,
  1554  				UpdatedAt: clock.Now(),
  1555  			},
  1556  		},
  1557  	}
  1558  	nudDisp.mu.Lock()
  1559  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1560  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1561  	}
  1562  	nudDisp.mu.Unlock()
  1563  }
  1564  
  1565  func TestEntryDelayToDelayWhenOverrideConfirmationWithSameAddress(t *testing.T) {
  1566  	c := DefaultNUDConfigurations()
  1567  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1568  
  1569  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1570  		t.Fatalf("unknownToStale(...) = %s", err)
  1571  	}
  1572  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1573  		t.Fatalf("staleToDelay(...) = %s", err)
  1574  	}
  1575  
  1576  	e.mu.Lock()
  1577  	e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
  1578  		Solicited: false,
  1579  		Override:  true,
  1580  		IsRouter:  false,
  1581  	})
  1582  	if e.mu.neigh.State != Delay {
  1583  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Delay)
  1584  	}
  1585  	if e.mu.neigh.LinkAddr != entryTestLinkAddr1 {
  1586  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, entryTestLinkAddr1)
  1587  	}
  1588  	e.mu.Unlock()
  1589  
  1590  	// No probes should have been sent.
  1591  	runImmediatelyScheduledJobs(clock)
  1592  	{
  1593  		linkRes.mu.Lock()
  1594  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1595  		linkRes.mu.Unlock()
  1596  		if diff != "" {
  1597  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1598  		}
  1599  	}
  1600  
  1601  	// No events should have been dispatched.
  1602  	nudDisp.mu.Lock()
  1603  	if diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events); diff != "" {
  1604  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1605  	}
  1606  	nudDisp.mu.Unlock()
  1607  }
  1608  
  1609  func TestEntryDelayToStaleWhenProbeWithDifferentAddress(t *testing.T) {
  1610  	c := DefaultNUDConfigurations()
  1611  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1612  
  1613  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1614  		t.Fatalf("unknownToStale(...) = %s", err)
  1615  	}
  1616  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1617  		t.Fatalf("staleToDelay(...) = %s", err)
  1618  	}
  1619  
  1620  	e.mu.Lock()
  1621  	e.handleProbeLocked(entryTestLinkAddr2)
  1622  	if e.mu.neigh.State != Stale {
  1623  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1624  	}
  1625  	e.mu.Unlock()
  1626  
  1627  	// No probes should have been sent.
  1628  	runImmediatelyScheduledJobs(clock)
  1629  	{
  1630  		linkRes.mu.Lock()
  1631  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1632  		linkRes.mu.Unlock()
  1633  		if diff != "" {
  1634  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1635  		}
  1636  	}
  1637  
  1638  	wantEvents := []testEntryEventInfo{
  1639  		{
  1640  			EventType: entryTestChanged,
  1641  			NICID:     entryTestNICID,
  1642  			Entry: NeighborEntry{
  1643  				Addr:      entryTestAddr1,
  1644  				LinkAddr:  entryTestLinkAddr2,
  1645  				State:     Stale,
  1646  				UpdatedAt: clock.Now(),
  1647  			},
  1648  		},
  1649  	}
  1650  	nudDisp.mu.Lock()
  1651  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1652  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1653  	}
  1654  	nudDisp.mu.Unlock()
  1655  }
  1656  
  1657  func TestEntryDelayToStaleWhenConfirmationWithDifferentAddress(t *testing.T) {
  1658  	c := DefaultNUDConfigurations()
  1659  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1660  
  1661  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1662  		t.Fatalf("unknownToStale(...) = %s", err)
  1663  	}
  1664  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1665  		t.Fatalf("staleToDelay(...) = %s", err)
  1666  	}
  1667  
  1668  	e.mu.Lock()
  1669  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1670  		Solicited: false,
  1671  		Override:  true,
  1672  		IsRouter:  false,
  1673  	})
  1674  	if e.mu.neigh.State != Stale {
  1675  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1676  	}
  1677  	e.mu.Unlock()
  1678  
  1679  	// No probes should have been sent.
  1680  	runImmediatelyScheduledJobs(clock)
  1681  	{
  1682  		linkRes.mu.Lock()
  1683  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1684  		linkRes.mu.Unlock()
  1685  		if diff != "" {
  1686  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1687  		}
  1688  	}
  1689  
  1690  	wantEvents := []testEntryEventInfo{
  1691  		{
  1692  			EventType: entryTestChanged,
  1693  			NICID:     entryTestNICID,
  1694  			Entry: NeighborEntry{
  1695  				Addr:      entryTestAddr1,
  1696  				LinkAddr:  entryTestLinkAddr2,
  1697  				State:     Stale,
  1698  				UpdatedAt: clock.Now(),
  1699  			},
  1700  		},
  1701  	}
  1702  	nudDisp.mu.Lock()
  1703  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1704  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1705  	}
  1706  	nudDisp.mu.Unlock()
  1707  }
  1708  
  1709  func TestEntryDelayToProbe(t *testing.T) {
  1710  	c := DefaultNUDConfigurations()
  1711  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1712  
  1713  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1714  		t.Fatalf("unknownToStale(...) = %s", err)
  1715  	}
  1716  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1717  		t.Fatalf("staleToDelay(...) = %s", err)
  1718  	}
  1719  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  1720  		t.Fatalf("delayToProbe(...) = %s", err)
  1721  	}
  1722  }
  1723  
  1724  func delayToProbe(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
  1725  	{
  1726  		e.mu.Lock()
  1727  		state := e.mu.neigh.State
  1728  		e.mu.Unlock()
  1729  		if state != Delay {
  1730  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Delay)
  1731  		}
  1732  	}
  1733  
  1734  	// Wait for the first unicast probe to be transmitted, marking the
  1735  	// transition from Delay to Probe.
  1736  	clock.Advance(c.DelayFirstProbeTime)
  1737  
  1738  	{
  1739  		e.mu.Lock()
  1740  		state := e.mu.neigh.State
  1741  		e.mu.Unlock()
  1742  		if state != Probe {
  1743  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Probe)
  1744  		}
  1745  	}
  1746  
  1747  	wantProbes := []entryTestProbeInfo{
  1748  		{
  1749  			RemoteAddress:     entryTestAddr1,
  1750  			RemoteLinkAddress: entryTestLinkAddr1,
  1751  		},
  1752  	}
  1753  	{
  1754  		linkRes.mu.Lock()
  1755  		diff := cmp.Diff(wantProbes, linkRes.mu.probes)
  1756  		linkRes.mu.probes = nil
  1757  		linkRes.mu.Unlock()
  1758  		if diff != "" {
  1759  			return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1760  		}
  1761  	}
  1762  
  1763  	wantEvents := []testEntryEventInfo{
  1764  		{
  1765  			EventType: entryTestChanged,
  1766  			NICID:     entryTestNICID,
  1767  			Entry: NeighborEntry{
  1768  				Addr:      entryTestAddr1,
  1769  				LinkAddr:  entryTestLinkAddr1,
  1770  				State:     Probe,
  1771  				UpdatedAt: clock.Now(),
  1772  			},
  1773  		},
  1774  	}
  1775  	{
  1776  		nudDisp.mu.Lock()
  1777  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
  1778  		nudDisp.mu.events = nil
  1779  		nudDisp.mu.Unlock()
  1780  		if diff != "" {
  1781  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1782  		}
  1783  	}
  1784  
  1785  	return nil
  1786  }
  1787  
  1788  func TestEntryProbeToStaleWhenProbeWithDifferentAddress(t *testing.T) {
  1789  	c := DefaultNUDConfigurations()
  1790  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1791  
  1792  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1793  		t.Fatalf("unknownToStale(...) = %s", err)
  1794  	}
  1795  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1796  		t.Fatalf("staleToDelay(...) = %s", err)
  1797  	}
  1798  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  1799  		t.Fatalf("delayToProbe(...) = %s", err)
  1800  	}
  1801  
  1802  	e.mu.Lock()
  1803  	e.handleProbeLocked(entryTestLinkAddr2)
  1804  	if e.mu.neigh.State != Stale {
  1805  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1806  	}
  1807  	e.mu.Unlock()
  1808  
  1809  	// No probes should have been sent.
  1810  	runImmediatelyScheduledJobs(clock)
  1811  	{
  1812  		linkRes.mu.Lock()
  1813  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1814  		linkRes.mu.Unlock()
  1815  		if diff != "" {
  1816  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1817  		}
  1818  	}
  1819  
  1820  	wantEvents := []testEntryEventInfo{
  1821  		{
  1822  			EventType: entryTestChanged,
  1823  			NICID:     entryTestNICID,
  1824  			Entry: NeighborEntry{
  1825  				Addr:      entryTestAddr1,
  1826  				LinkAddr:  entryTestLinkAddr2,
  1827  				State:     Stale,
  1828  				UpdatedAt: clock.Now(),
  1829  			},
  1830  		},
  1831  	}
  1832  	nudDisp.mu.Lock()
  1833  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1834  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1835  	}
  1836  	nudDisp.mu.Unlock()
  1837  }
  1838  
  1839  func TestEntryProbeToStaleWhenConfirmationWithDifferentAddress(t *testing.T) {
  1840  	c := DefaultNUDConfigurations()
  1841  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1842  
  1843  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1844  		t.Fatalf("unknownToStale(...) = %s", err)
  1845  	}
  1846  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1847  		t.Fatalf("staleToDelay(...) = %s", err)
  1848  	}
  1849  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  1850  		t.Fatalf("delayToProbe(...) = %s", err)
  1851  	}
  1852  
  1853  	e.mu.Lock()
  1854  	e.handleConfirmationLocked(entryTestLinkAddr2, ReachabilityConfirmationFlags{
  1855  		Solicited: false,
  1856  		Override:  true,
  1857  		IsRouter:  false,
  1858  	})
  1859  	if e.mu.neigh.State != Stale {
  1860  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Stale)
  1861  	}
  1862  	e.mu.Unlock()
  1863  
  1864  	// No probes should have been sent.
  1865  	runImmediatelyScheduledJobs(clock)
  1866  	{
  1867  		linkRes.mu.Lock()
  1868  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1869  		linkRes.mu.Unlock()
  1870  		if diff != "" {
  1871  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1872  		}
  1873  	}
  1874  
  1875  	wantEvents := []testEntryEventInfo{
  1876  		{
  1877  			EventType: entryTestChanged,
  1878  			NICID:     entryTestNICID,
  1879  			Entry: NeighborEntry{
  1880  				Addr:      entryTestAddr1,
  1881  				LinkAddr:  entryTestLinkAddr2,
  1882  				State:     Stale,
  1883  				UpdatedAt: clock.Now(),
  1884  			},
  1885  		},
  1886  	}
  1887  	nudDisp.mu.Lock()
  1888  	if diff := cmp.Diff(wantEvents, nudDisp.mu.events); diff != "" {
  1889  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1890  	}
  1891  	nudDisp.mu.Unlock()
  1892  }
  1893  
  1894  func TestEntryProbeToProbeWhenOverrideConfirmationWithSameAddress(t *testing.T) {
  1895  	c := DefaultNUDConfigurations()
  1896  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1897  
  1898  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1899  		t.Fatalf("unknownToStale(...) = %s", err)
  1900  	}
  1901  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1902  		t.Fatalf("staleToDelay(...) = %s", err)
  1903  	}
  1904  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  1905  		t.Fatalf("delayToProbe(...) = %s", err)
  1906  	}
  1907  
  1908  	e.mu.Lock()
  1909  	e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
  1910  		Solicited: false,
  1911  		Override:  true,
  1912  		IsRouter:  false,
  1913  	})
  1914  	if e.mu.neigh.State != Probe {
  1915  		t.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe)
  1916  	}
  1917  	if got, want := e.mu.neigh.LinkAddr, entryTestLinkAddr1; got != want {
  1918  		t.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", got, want)
  1919  	}
  1920  	e.mu.Unlock()
  1921  
  1922  	// No probes should have been sent.
  1923  	runImmediatelyScheduledJobs(clock)
  1924  	{
  1925  		linkRes.mu.Lock()
  1926  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1927  		linkRes.mu.Unlock()
  1928  		if diff != "" {
  1929  			t.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1930  		}
  1931  	}
  1932  
  1933  	// No events should have been dispatched.
  1934  	nudDisp.mu.Lock()
  1935  	diff := cmp.Diff([]testEntryEventInfo(nil), nudDisp.mu.events)
  1936  	nudDisp.mu.Unlock()
  1937  	if diff != "" {
  1938  		t.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  1939  	}
  1940  }
  1941  
  1942  func TestEntryProbeToReachableWhenSolicitedOverrideConfirmation(t *testing.T) {
  1943  	c := DefaultNUDConfigurations()
  1944  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  1945  
  1946  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  1947  		t.Fatalf("unknownToStale(...) = %s", err)
  1948  	}
  1949  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  1950  		t.Fatalf("staleToDelay(...) = %s", err)
  1951  	}
  1952  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  1953  		t.Fatalf("delayToProbe(...) = %s", err)
  1954  	}
  1955  	if err := probeToReachableWithOverride(e, nudDisp, linkRes, clock, entryTestLinkAddr2); err != nil {
  1956  		t.Fatalf("probeToReachableWithOverride(...) = %s", err)
  1957  	}
  1958  }
  1959  
  1960  func probeToReachableWithFlags(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) error {
  1961  	if err := func() error {
  1962  		e.mu.Lock()
  1963  		defer e.mu.Unlock()
  1964  
  1965  		prevLinkAddr := e.mu.neigh.LinkAddr
  1966  		if e.mu.neigh.State != Probe {
  1967  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Probe)
  1968  		}
  1969  		e.handleConfirmationLocked(linkAddr, flags)
  1970  
  1971  		if e.mu.neigh.State != Reachable {
  1972  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Reachable)
  1973  		}
  1974  		if linkAddr == "" {
  1975  			linkAddr = prevLinkAddr
  1976  		}
  1977  		if e.mu.neigh.LinkAddr != linkAddr {
  1978  			return fmt.Errorf("got e.mu.neigh.LinkAddr = %q, want = %q", e.mu.neigh.LinkAddr, linkAddr)
  1979  		}
  1980  		return nil
  1981  	}(); err != nil {
  1982  		return err
  1983  	}
  1984  
  1985  	// No probes should have been sent.
  1986  	runImmediatelyScheduledJobs(clock)
  1987  	{
  1988  		linkRes.mu.Lock()
  1989  		diff := cmp.Diff([]entryTestProbeInfo(nil), linkRes.mu.probes)
  1990  		linkRes.mu.Unlock()
  1991  		if diff != "" {
  1992  			return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  1993  		}
  1994  	}
  1995  
  1996  	wantEvents := []testEntryEventInfo{
  1997  		{
  1998  			EventType: entryTestChanged,
  1999  			NICID:     entryTestNICID,
  2000  			Entry: NeighborEntry{
  2001  				Addr:      entryTestAddr1,
  2002  				LinkAddr:  linkAddr,
  2003  				State:     Reachable,
  2004  				UpdatedAt: clock.Now(),
  2005  			},
  2006  		},
  2007  	}
  2008  	{
  2009  		nudDisp.mu.Lock()
  2010  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
  2011  		nudDisp.mu.events = nil
  2012  		nudDisp.mu.Unlock()
  2013  		if diff != "" {
  2014  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  2015  		}
  2016  	}
  2017  
  2018  	return nil
  2019  }
  2020  
  2021  func probeToReachableWithOverride(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock, linkAddr tcpip.LinkAddress) error {
  2022  	return probeToReachableWithFlags(e, nudDisp, linkRes, clock, linkAddr, ReachabilityConfirmationFlags{
  2023  		Solicited: true,
  2024  		Override:  true,
  2025  		IsRouter:  false,
  2026  	})
  2027  }
  2028  
  2029  func TestEntryProbeToReachableWhenSolicitedConfirmationWithSameAddress(t *testing.T) {
  2030  	c := DefaultNUDConfigurations()
  2031  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  2032  
  2033  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  2034  		t.Fatalf("unknownToStale(...) = %s", err)
  2035  	}
  2036  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  2037  		t.Fatalf("staleToDelay(...) = %s", err)
  2038  	}
  2039  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  2040  		t.Fatalf("delayToProbe(...) = %s", err)
  2041  	}
  2042  	if err := probeToReachable(e, nudDisp, linkRes, clock); err != nil {
  2043  		t.Fatalf("probeToReachable(...) = %s", err)
  2044  	}
  2045  }
  2046  
  2047  func probeToReachable(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
  2048  	return probeToReachableWithFlags(e, nudDisp, linkRes, clock, entryTestLinkAddr1, ReachabilityConfirmationFlags{
  2049  		Solicited: true,
  2050  		Override:  false,
  2051  		IsRouter:  false,
  2052  	})
  2053  }
  2054  
  2055  func TestEntryProbeToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) {
  2056  	c := DefaultNUDConfigurations()
  2057  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  2058  
  2059  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  2060  		t.Fatalf("unknownToStale(...) = %s", err)
  2061  	}
  2062  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  2063  		t.Fatalf("staleToDelay(...) = %s", err)
  2064  	}
  2065  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  2066  		t.Fatalf("delayToProbe(...) = %s", err)
  2067  	}
  2068  	if err := probeToReachableWithoutAddress(e, nudDisp, linkRes, clock); err != nil {
  2069  		t.Fatalf("probeToReachableWithoutAddress(...) = %s", err)
  2070  	}
  2071  }
  2072  
  2073  func probeToReachableWithoutAddress(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
  2074  	return probeToReachableWithFlags(e, nudDisp, linkRes, clock, "" /* linkAddr */, ReachabilityConfirmationFlags{
  2075  		Solicited: true,
  2076  		Override:  false,
  2077  		IsRouter:  false,
  2078  	})
  2079  }
  2080  
  2081  func TestEntryProbeToUnreachable(t *testing.T) {
  2082  	c := DefaultNUDConfigurations()
  2083  	c.MaxMulticastProbes = 3
  2084  	c.MaxUnicastProbes = 3
  2085  	c.DelayFirstProbeTime = c.RetransmitTimer
  2086  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  2087  
  2088  	if err := unknownToStale(e, nudDisp, linkRes, clock); err != nil {
  2089  		t.Fatalf("unknownToStale(...) = %s", err)
  2090  	}
  2091  	if err := staleToDelay(e, nudDisp, linkRes, clock); err != nil {
  2092  		t.Fatalf("staleToDelay(...) = %s", err)
  2093  	}
  2094  	if err := delayToProbe(c, e, nudDisp, linkRes, clock); err != nil {
  2095  		t.Fatalf("delayToProbe(...) = %s", err)
  2096  	}
  2097  	if err := probeToUnreachable(c, e, nudDisp, linkRes, clock); err != nil {
  2098  		t.Fatalf("probeToUnreachable(...) = %s", err)
  2099  	}
  2100  }
  2101  
  2102  func probeToUnreachable(c NUDConfigurations, e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
  2103  	{
  2104  		e.mu.Lock()
  2105  		state := e.mu.neigh.State
  2106  		e.mu.Unlock()
  2107  		if state != Probe {
  2108  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Probe)
  2109  		}
  2110  	}
  2111  
  2112  	// The first probe was sent in the transition from Delay to Probe.
  2113  	clock.Advance(c.RetransmitTimer)
  2114  
  2115  	// Observe each subsequent unicast probe transmitted.
  2116  	for i := uint32(1); i < c.MaxUnicastProbes; i++ {
  2117  		wantProbes := []entryTestProbeInfo{{
  2118  			RemoteAddress:     entryTestAddr1,
  2119  			RemoteLinkAddress: entryTestLinkAddr1,
  2120  		}}
  2121  		linkRes.mu.Lock()
  2122  		diff := cmp.Diff(wantProbes, linkRes.mu.probes)
  2123  		linkRes.mu.probes = nil
  2124  		linkRes.mu.Unlock()
  2125  		if diff != "" {
  2126  			return fmt.Errorf("link address resolver probe #%d mismatch (-want, +got):\n%s", i+1, diff)
  2127  		}
  2128  
  2129  		e.mu.Lock()
  2130  		state := e.mu.neigh.State
  2131  		e.mu.Unlock()
  2132  		if state != Probe {
  2133  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Probe)
  2134  		}
  2135  
  2136  		clock.Advance(c.RetransmitTimer)
  2137  	}
  2138  
  2139  	{
  2140  		e.mu.Lock()
  2141  		state := e.mu.neigh.State
  2142  		e.mu.Unlock()
  2143  		if state != Unreachable {
  2144  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", state, Unreachable)
  2145  		}
  2146  	}
  2147  
  2148  	wantEvents := []testEntryEventInfo{
  2149  		{
  2150  			EventType: entryTestChanged,
  2151  			NICID:     entryTestNICID,
  2152  			Entry: NeighborEntry{
  2153  				Addr:      entryTestAddr1,
  2154  				LinkAddr:  entryTestLinkAddr1,
  2155  				State:     Unreachable,
  2156  				UpdatedAt: clock.Now(),
  2157  			},
  2158  		},
  2159  	}
  2160  	nudDisp.mu.Lock()
  2161  	diff := cmp.Diff(wantEvents, nudDisp.mu.events)
  2162  	nudDisp.mu.events = nil
  2163  	nudDisp.mu.Unlock()
  2164  	if diff != "" {
  2165  		return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  2166  	}
  2167  
  2168  	return nil
  2169  }
  2170  
  2171  func TestEntryUnreachableToIncomplete(t *testing.T) {
  2172  	c := DefaultNUDConfigurations()
  2173  	c.MaxMulticastProbes = 3
  2174  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  2175  
  2176  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
  2177  		t.Fatalf("unknownToIncomplete(...) = %s", err)
  2178  	}
  2179  	if err := incompleteToUnreachable(c, e, nudDisp, linkRes, clock); err != nil {
  2180  		t.Fatalf("incompleteToUnreachable(...) = %s", err)
  2181  	}
  2182  	if err := unreachableToIncomplete(e, nudDisp, linkRes, clock); err != nil {
  2183  		t.Fatalf("unreachableToIncomplete(...) = %s", err)
  2184  	}
  2185  }
  2186  
  2187  func unreachableToIncomplete(e *neighborEntry, nudDisp *testNUDDispatcher, linkRes *entryTestLinkResolver, clock *faketime.ManualClock) error {
  2188  	if err := func() error {
  2189  		e.mu.Lock()
  2190  		defer e.mu.Unlock()
  2191  
  2192  		if e.mu.neigh.State != Unreachable {
  2193  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Unreachable)
  2194  		}
  2195  		e.handlePacketQueuedLocked(entryTestAddr2)
  2196  		if e.mu.neigh.State != Incomplete {
  2197  			return fmt.Errorf("got e.mu.neigh.State = %q, want = %q", e.mu.neigh.State, Incomplete)
  2198  		}
  2199  		return nil
  2200  	}(); err != nil {
  2201  		return err
  2202  	}
  2203  
  2204  	runImmediatelyScheduledJobs(clock)
  2205  	wantProbes := []entryTestProbeInfo{
  2206  		{
  2207  			RemoteAddress:     entryTestAddr1,
  2208  			RemoteLinkAddress: tcpip.LinkAddress(""),
  2209  			LocalAddress:      entryTestAddr2,
  2210  		},
  2211  	}
  2212  	linkRes.mu.Lock()
  2213  	diff := cmp.Diff(wantProbes, linkRes.mu.probes)
  2214  	linkRes.mu.probes = nil
  2215  	linkRes.mu.Unlock()
  2216  	if diff != "" {
  2217  		return fmt.Errorf("link address resolver probes mismatch (-want, +got):\n%s", diff)
  2218  	}
  2219  
  2220  	wantEvents := []testEntryEventInfo{
  2221  		{
  2222  			EventType: entryTestChanged,
  2223  			NICID:     entryTestNICID,
  2224  			Entry: NeighborEntry{
  2225  				Addr:      entryTestAddr1,
  2226  				LinkAddr:  tcpip.LinkAddress(""),
  2227  				State:     Incomplete,
  2228  				UpdatedAt: clock.Now(),
  2229  			},
  2230  		},
  2231  	}
  2232  	{
  2233  		nudDisp.mu.Lock()
  2234  		diff := cmp.Diff(wantEvents, nudDisp.mu.events)
  2235  		nudDisp.mu.events = nil
  2236  		nudDisp.mu.Unlock()
  2237  		if diff != "" {
  2238  			return fmt.Errorf("nud dispatcher events mismatch (-want, +got):\n%s", diff)
  2239  		}
  2240  	}
  2241  	return nil
  2242  }
  2243  
  2244  func TestEntryUnreachableToStale(t *testing.T) {
  2245  	c := DefaultNUDConfigurations()
  2246  	c.MaxMulticastProbes = 3
  2247  	// Eliminate random factors from ReachableTime computation so the transition
  2248  	// from Stale to Reachable will only take BaseReachableTime duration.
  2249  	c.MinRandomFactor = 1
  2250  	c.MaxRandomFactor = 1
  2251  
  2252  	e, nudDisp, linkRes, clock := entryTestSetup(c)
  2253  
  2254  	if err := unknownToIncomplete(e, nudDisp, linkRes, clock); err != nil {
  2255  		t.Fatalf("unknownToIncomplete(...) = %s", err)
  2256  	}
  2257  	if err := incompleteToUnreachable(c, e, nudDisp, linkRes, clock); err != nil {
  2258  		t.Fatalf("incompleteToUnreachable(...) = %s", err)
  2259  	}
  2260  	if err := unreachableToIncomplete(e, nudDisp, linkRes, clock); err != nil {
  2261  		t.Fatalf("unreachableToIncomplete(...) = %s", err)
  2262  	}
  2263  	if err := incompleteToReachable(e, nudDisp, linkRes, clock); err != nil {
  2264  		t.Fatalf("incompleteToReachable(...) = %s", err)
  2265  	}
  2266  	if err := reachableToStale(c, e, nudDisp, linkRes, clock); err != nil {
  2267  		t.Fatalf("reachableToStale(...) = %s", err)
  2268  	}
  2269  }