Process NAs without target link-layer addresses

RFC 4861 section 4.4 comments the Target link-layer address option is sometimes
optional in a Neighbor Advertisement packet:

  "When responding to a unicast Neighbor Solicitation this option SHOULD be
  included."

Tests:
 pkg/tcpip/stack:stack_test
 - TestEntryStaleToReachableWhenSolicitedConfirmationWithoutAddress
 - TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress
 - TestEntryProbeToReachableWhenSolicitedConfirmationWithoutAddress
 pkg/tcpip/network/ipv6:ipv6_test
 - TestCallsToNeighborCache
PiperOrigin-RevId: 337396493
This commit is contained in:
Sam Balana
2020-10-15 15:37:01 -07:00
committed by gVisor bot
parent f0f7431ea2
commit 3269cefd6f
4 changed files with 540 additions and 15 deletions
+9 -9
View File
@@ -439,19 +439,19 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
// If the NA message has the target link layer option, update the link
// address cache with the link address for the target of the message.
if len(targetLinkAddr) != 0 {
if e.nud == nil {
if e.nud == nil {
if len(targetLinkAddr) != 0 {
e.linkAddrCache.AddLinkAddress(e.nic.ID(), targetAddr, targetLinkAddr)
return
}
e.nud.HandleConfirmation(targetAddr, targetLinkAddr, stack.ReachabilityConfirmationFlags{
Solicited: na.SolicitedFlag(),
Override: na.OverrideFlag(),
IsRouter: na.RouterFlag(),
})
return
}
e.nud.HandleConfirmation(targetAddr, targetLinkAddr, stack.ReachabilityConfirmationFlags{
Solicited: na.SolicitedFlag(),
Override: na.OverrideFlag(),
IsRouter: na.RouterFlag(),
})
case header.ICMPv6EchoRequest:
received.EchoRequest.Increment()
icmpHdr, ok := pkt.TransportHeader().Consume(header.ICMPv6EchoMinimumSize)
+251 -3
View File
@@ -101,14 +101,19 @@ func (*stubLinkAddressCache) CheckLocalAddress(tcpip.NICID, tcpip.NetworkProtoco
func (*stubLinkAddressCache) AddLinkAddress(tcpip.NICID, tcpip.Address, tcpip.LinkAddress) {
}
type stubNUDHandler struct{}
type stubNUDHandler struct {
probeCount int
confirmationCount int
}
var _ stack.NUDHandler = (*stubNUDHandler)(nil)
func (*stubNUDHandler) HandleProbe(remoteAddr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress, linkRes stack.LinkAddressResolver) {
func (s *stubNUDHandler) HandleProbe(remoteAddr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress, linkRes stack.LinkAddressResolver) {
s.probeCount++
}
func (*stubNUDHandler) HandleConfirmation(addr tcpip.Address, linkAddr tcpip.LinkAddress, flags stack.ReachabilityConfirmationFlags) {
func (s *stubNUDHandler) HandleConfirmation(addr tcpip.Address, linkAddr tcpip.LinkAddress, flags stack.ReachabilityConfirmationFlags) {
s.confirmationCount++
}
func (*stubNUDHandler) HandleUpperLevelConfirmation(addr tcpip.Address) {
@@ -118,6 +123,12 @@ var _ stack.NetworkInterface = (*testInterface)(nil)
type testInterface struct {
stack.NetworkLinkEndpoint
linkAddr tcpip.LinkAddress
}
func (i *testInterface) LinkAddress() tcpip.LinkAddress {
return i.linkAddr
}
func (*testInterface) ID() tcpip.NICID {
@@ -1492,3 +1503,240 @@ func TestPacketQueing(t *testing.T) {
})
}
}
func TestCallsToNeighborCache(t *testing.T) {
tests := []struct {
name string
createPacket func() header.ICMPv6
multicast bool
source tcpip.Address
destination tcpip.Address
wantProbeCount int
wantConfirmationCount int
}{
{
name: "Unicast Neighbor Solicitation without source link-layer address option",
createPacket: func() header.ICMPv6 {
nsSize := header.ICMPv6NeighborSolicitMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(nsSize))
icmp.SetType(header.ICMPv6NeighborSolicit)
ns := header.NDPNeighborSolicit(icmp.NDPPayload())
ns.SetTargetAddress(lladdr0)
return icmp
},
source: lladdr1,
destination: lladdr0,
// "The source link-layer address option SHOULD be included in unicast
// solicitations." - RFC 4861 section 4.3
//
// A Neighbor Advertisement needs to be sent in response, but the
// Neighbor Cache shouldn't be updated since we have no useful
// information about the sender.
wantProbeCount: 0,
},
{
name: "Unicast Neighbor Solicitation with source link-layer address option",
createPacket: func() header.ICMPv6 {
nsSize := header.ICMPv6NeighborSolicitMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(nsSize))
icmp.SetType(header.ICMPv6NeighborSolicit)
ns := header.NDPNeighborSolicit(icmp.NDPPayload())
ns.SetTargetAddress(lladdr0)
ns.Options().Serialize(header.NDPOptionsSerializer{
header.NDPSourceLinkLayerAddressOption(linkAddr1),
})
return icmp
},
source: lladdr1,
destination: lladdr0,
wantProbeCount: 1,
},
{
name: "Multicast Neighbor Solicitation without source link-layer address option",
createPacket: func() header.ICMPv6 {
nsSize := header.ICMPv6NeighborSolicitMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(nsSize))
icmp.SetType(header.ICMPv6NeighborSolicit)
ns := header.NDPNeighborSolicit(icmp.NDPPayload())
ns.SetTargetAddress(lladdr0)
return icmp
},
source: lladdr1,
destination: header.SolicitedNodeAddr(lladdr0),
// "The source link-layer address option MUST be included in multicast
// solicitations." - RFC 4861 section 4.3
wantProbeCount: 0,
},
{
name: "Multicast Neighbor Solicitation with source link-layer address option",
createPacket: func() header.ICMPv6 {
nsSize := header.ICMPv6NeighborSolicitMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(nsSize))
icmp.SetType(header.ICMPv6NeighborSolicit)
ns := header.NDPNeighborSolicit(icmp.NDPPayload())
ns.SetTargetAddress(lladdr0)
ns.Options().Serialize(header.NDPOptionsSerializer{
header.NDPSourceLinkLayerAddressOption(linkAddr1),
})
return icmp
},
source: lladdr1,
destination: header.SolicitedNodeAddr(lladdr0),
wantProbeCount: 1,
},
{
name: "Unicast Neighbor Advertisement without target link-layer address option",
createPacket: func() header.ICMPv6 {
naSize := header.ICMPv6NeighborAdvertMinimumSize
icmp := header.ICMPv6(buffer.NewView(naSize))
icmp.SetType(header.ICMPv6NeighborAdvert)
na := header.NDPNeighborAdvert(icmp.NDPPayload())
na.SetSolicitedFlag(true)
na.SetOverrideFlag(false)
na.SetTargetAddress(lladdr1)
return icmp
},
source: lladdr1,
destination: lladdr0,
// "When responding to unicast solicitations, the target link-layer
// address option can be omitted since the sender of the solicitation has
// the correct link-layer address; otherwise, it would not be able to
// send the unicast solicitation in the first place."
// - RFC 4861 section 4.4
wantConfirmationCount: 1,
},
{
name: "Unicast Neighbor Advertisement with target link-layer address option",
createPacket: func() header.ICMPv6 {
naSize := header.ICMPv6NeighborAdvertMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(naSize))
icmp.SetType(header.ICMPv6NeighborAdvert)
na := header.NDPNeighborAdvert(icmp.NDPPayload())
na.SetSolicitedFlag(true)
na.SetOverrideFlag(false)
na.SetTargetAddress(lladdr1)
na.Options().Serialize(header.NDPOptionsSerializer{
header.NDPTargetLinkLayerAddressOption(linkAddr1),
})
return icmp
},
source: lladdr1,
destination: lladdr0,
wantConfirmationCount: 1,
},
{
name: "Multicast Neighbor Advertisement without target link-layer address option",
createPacket: func() header.ICMPv6 {
naSize := header.ICMPv6NeighborAdvertMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(naSize))
icmp.SetType(header.ICMPv6NeighborAdvert)
na := header.NDPNeighborAdvert(icmp.NDPPayload())
na.SetSolicitedFlag(false)
na.SetOverrideFlag(false)
na.SetTargetAddress(lladdr1)
return icmp
},
source: lladdr1,
destination: header.IPv6AllNodesMulticastAddress,
// "Target link-layer address MUST be included for multicast solicitations
// in order to avoid infinite Neighbor Solicitation "recursion" when the
// peer node does not have a cache entry to return a Neighbor
// Advertisements message." - RFC 4861 section 4.4
wantConfirmationCount: 0,
},
{
name: "Multicast Neighbor Advertisement with target link-layer address option",
createPacket: func() header.ICMPv6 {
naSize := header.ICMPv6NeighborAdvertMinimumSize + header.NDPLinkLayerAddressSize
icmp := header.ICMPv6(buffer.NewView(naSize))
icmp.SetType(header.ICMPv6NeighborAdvert)
na := header.NDPNeighborAdvert(icmp.NDPPayload())
na.SetSolicitedFlag(false)
na.SetOverrideFlag(false)
na.SetTargetAddress(lladdr1)
na.Options().Serialize(header.NDPOptionsSerializer{
header.NDPTargetLinkLayerAddressOption(linkAddr1),
})
return icmp
},
source: lladdr1,
destination: header.IPv6AllNodesMulticastAddress,
wantConfirmationCount: 1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
UseNeighborCache: true,
})
{
if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
t.Fatalf("CreateNIC(_, _) = %s", err)
}
if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, lladdr0, err)
}
}
{
subnet, err := tcpip.NewSubnet(lladdr1, tcpip.AddressMask(strings.Repeat("\xff", len(lladdr1))))
if err != nil {
t.Fatal(err)
}
s.SetRouteTable(
[]tcpip.Route{{
Destination: subnet,
NIC: nicID,
}},
)
}
netProto := s.NetworkProtocolInstance(ProtocolNumber)
if netProto == nil {
t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
}
nudHandler := &stubNUDHandler{}
ep := netProto.NewEndpoint(&testInterface{linkAddr: linkAddr0}, &stubLinkAddressCache{}, nudHandler, &stubDispatcher{})
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
r, err := s.FindRoute(nicID, lladdr0, test.source, ProtocolNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(%d, %s, %s, _, false) = (_, %s), want = (_, nil)", nicID, lladdr0, lladdr1, err)
}
defer r.Release()
// TODO(gvisor.dev/issue/4517): Remove the need for this manual patch.
r.LocalAddress = test.destination
icmp := test.createPacket()
icmp.SetChecksum(header.ICMPv6Checksum(icmp, r.RemoteAddress, r.LocalAddress, buffer.VectorisedView{}))
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: header.IPv6MinimumSize,
Data: buffer.View(icmp).ToVectorisedView(),
})
ip := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))
ip.Encode(&header.IPv6Fields{
PayloadLength: uint16(len(icmp)),
NextHeader: uint8(header.ICMPv6ProtocolNumber),
HopLimit: header.NDPHopLimit,
SrcAddr: r.RemoteAddress,
DstAddr: r.LocalAddress,
})
ep.HandlePacket(&r, pkt)
// Confirm the endpoint calls the correct NUDHandler method.
if nudHandler.probeCount != test.wantProbeCount {
t.Errorf("got nudHandler.probeCount = %d, want = %d", nudHandler.probeCount, test.wantProbeCount)
}
if nudHandler.confirmationCount != test.wantConfirmationCount {
t.Errorf("got nudHandler.confirmationCount = %d, want = %d", nudHandler.confirmationCount, test.wantConfirmationCount)
}
})
}
}
+3 -3
View File
@@ -406,9 +406,9 @@ func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, fla
// INCOMPLETE state." - RFC 4861 section 7.2.5
case Reachable, Stale, Delay, Probe:
sameLinkAddr := e.neigh.LinkAddr == linkAddr
isLinkAddrDifferent := len(linkAddr) != 0 && e.neigh.LinkAddr != linkAddr
if !sameLinkAddr {
if isLinkAddrDifferent {
if !flags.Override {
if e.neigh.State == Reachable {
e.dispatchChangeEventLocked(Stale)
@@ -431,7 +431,7 @@ func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, fla
}
}
if flags.Solicited && (flags.Override || sameLinkAddr) {
if flags.Solicited && (flags.Override || !isLinkAddrDifferent) {
if e.neigh.State != Reachable {
e.dispatchChangeEventLocked(Reachable)
}
+277
View File
@@ -83,15 +83,18 @@ func eventDiffOptsWithSort() []cmp.Option {
// | Reachable | Stale | Reachable timer expired | | Changed |
// | Reachable | Stale | Probe or confirmation w/ different address | | Changed |
// | Stale | Reachable | Solicited override confirmation | Update LinkAddr | Changed |
// | Stale | Reachable | Solicited confirmation w/o address | Notify wakers | Changed |
// | Stale | Stale | Override confirmation | Update LinkAddr | Changed |
// | Stale | Stale | Probe w/ different address | Update LinkAddr | Changed |
// | Stale | Delay | Packet sent | | Changed |
// | Delay | Reachable | Upper-layer confirmation | | Changed |
// | Delay | Reachable | Solicited override confirmation | Update LinkAddr | Changed |
// | Delay | Reachable | Solicited confirmation w/o address | Notify wakers | Changed |
// | Delay | Stale | Probe or confirmation w/ different address | | Changed |
// | Delay | Probe | Delay timer expired | Send probe | Changed |
// | Probe | Reachable | Solicited override confirmation | Update LinkAddr | Changed |
// | Probe | Reachable | Solicited confirmation w/ same address | Notify wakers | Changed |
// | Probe | Reachable | Solicited confirmation w/o address | Notify wakers | Changed |
// | Probe | Stale | Probe or confirmation w/ different address | | Changed |
// | Probe | Probe | Retransmit timer expired | Send probe | Changed |
// | Probe | Failed | Max probes sent without reply | Notify wakers | Removed |
@@ -1370,6 +1373,77 @@ func TestEntryStaleToReachableWhenSolicitedOverrideConfirmation(t *testing.T) {
nudDisp.mu.Unlock()
}
func TestEntryStaleToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) {
c := DefaultNUDConfigurations()
e, nudDisp, linkRes, _ := entryTestSetup(c)
e.mu.Lock()
e.handlePacketQueuedLocked()
e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
Solicited: false,
Override: false,
IsRouter: false,
})
if e.neigh.State != Stale {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Stale)
}
e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{
Solicited: true,
Override: false,
IsRouter: false,
})
if e.neigh.State != Reachable {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Reachable)
}
if e.neigh.LinkAddr != entryTestLinkAddr1 {
t.Errorf("got e.neigh.LinkAddr = %q, want = %q", e.neigh.LinkAddr, entryTestLinkAddr1)
}
e.mu.Unlock()
wantProbes := []entryTestProbeInfo{
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: tcpip.LinkAddress(""),
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)
}
wantEvents := []testEntryEventInfo{
{
EventType: entryTestAdded,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: tcpip.LinkAddress(""),
State: Incomplete,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
State: Stale,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
State: Reachable,
},
}
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 TestEntryStaleToStaleWhenOverrideConfirmation(t *testing.T) {
c := DefaultNUDConfigurations()
e, nudDisp, linkRes, _ := entryTestSetup(c)
@@ -1752,6 +1826,100 @@ func TestEntryDelayToReachableWhenSolicitedOverrideConfirmation(t *testing.T) {
nudDisp.mu.Unlock()
}
func TestEntryDelayToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) {
c := DefaultNUDConfigurations()
c.MaxMulticastProbes = 1
// Eliminate random factors from ReachableTime computation so the transition
// from Stale to Reachable will only take BaseReachableTime duration.
c.MinRandomFactor = 1
c.MaxRandomFactor = 1
e, nudDisp, linkRes, clock := entryTestSetup(c)
e.mu.Lock()
e.handlePacketQueuedLocked()
e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
Solicited: false,
Override: false,
IsRouter: false,
})
e.handlePacketQueuedLocked()
if e.neigh.State != Delay {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Delay)
}
e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{
Solicited: true,
Override: false,
IsRouter: false,
})
if e.neigh.State != Reachable {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Reachable)
}
if e.neigh.LinkAddr != entryTestLinkAddr1 {
t.Errorf("got e.neigh.LinkAddr = %q, want = %q", e.neigh.LinkAddr, entryTestLinkAddr1)
}
e.mu.Unlock()
wantProbes := []entryTestProbeInfo{
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: tcpip.LinkAddress(""),
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)
}
clock.Advance(c.BaseReachableTime)
wantEvents := []testEntryEventInfo{
{
EventType: entryTestAdded,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: tcpip.LinkAddress(""),
State: Incomplete,
},
{
EventType: entryTestChanged,
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: Reachable,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
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 TestEntryStaysDelayWhenOverrideConfirmationWithSameAddress(t *testing.T) {
c := DefaultNUDConfigurations()
e, nudDisp, linkRes, _ := entryTestSetup(c)
@@ -2665,6 +2833,115 @@ func TestEntryProbeToReachableWhenSolicitedConfirmationWithSameAddress(t *testin
nudDisp.mu.Unlock()
}
func TestEntryProbeToReachableWhenSolicitedConfirmationWithoutAddress(t *testing.T) {
c := DefaultNUDConfigurations()
// Eliminate random factors from ReachableTime computation so the transition
// from Stale to Reachable will only take BaseReachableTime duration.
c.MinRandomFactor = 1
c.MaxRandomFactor = 1
e, nudDisp, linkRes, clock := entryTestSetup(c)
e.mu.Lock()
e.handlePacketQueuedLocked()
e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
Solicited: false,
Override: false,
IsRouter: false,
})
e.handlePacketQueuedLocked()
e.mu.Unlock()
clock.Advance(c.DelayFirstProbeTime)
wantProbes := []entryTestProbeInfo{
// The first probe is caused by the Unknown-to-Incomplete transition.
{
RemoteAddress: entryTestAddr1,
RemoteLinkAddress: tcpip.LinkAddress(""),
LocalAddress: entryTestAddr2,
},
// The second probe is 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 e.neigh.State != Probe {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Probe)
}
e.handleConfirmationLocked("" /* linkAddr */, ReachabilityConfirmationFlags{
Solicited: true,
Override: false,
IsRouter: false,
})
if e.neigh.State != Reachable {
t.Errorf("got e.neigh.State = %q, want = %q", e.neigh.State, Reachable)
}
e.mu.Unlock()
clock.Advance(c.BaseReachableTime)
wantEvents := []testEntryEventInfo{
{
EventType: entryTestAdded,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: tcpip.LinkAddress(""),
State: Incomplete,
},
{
EventType: entryTestChanged,
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: entryTestLinkAddr1,
State: Reachable,
},
{
EventType: entryTestChanged,
NICID: entryTestNICID,
Addr: entryTestAddr1,
LinkAddr: entryTestLinkAddr1,
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 TestEntryProbeToFailed(t *testing.T) {
c := DefaultNUDConfigurations()
c.MaxMulticastProbes = 3