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