mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Attempt SLAAC address regeneration on DAD conflicts
As per RFC 7217 section 6, attempt to regenerate IPv6 SLAAC address in response to a DAD conflict if the address was generated with an opaque IID as outlined in RFC 7217 section 5. Test: - stack_test.TestAutoGenAddrWithOpaqueIIDDADRetries - stack_test.TestAutoGenAddrWithEUI64IIDNoDADRetries - stack_test.TestAutoGenAddrContinuesLifetimesAfterRetry PiperOrigin-RevId: 306555645
This commit is contained in:
committed by
gVisor bot
parent
2dd6384de8
commit
36fbaac520
@@ -34,6 +34,7 @@ const (
|
||||
// The least significant 3 bytes are the same as addr2 so both addr2 and
|
||||
// addr3 will have the same solicited-node address.
|
||||
addr3 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x02"
|
||||
addr4 = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x03"
|
||||
|
||||
// Tests use the extension header identifier values as uint8 instead of
|
||||
// header.IPv6ExtensionHeaderIdentifier.
|
||||
@@ -167,6 +168,8 @@ func TestReceiveOnAllNodesMulticastAddr(t *testing.T) {
|
||||
// packets destined to the IPv6 solicited-node address of an assigned IPv6
|
||||
// address.
|
||||
func TestReceiveOnSolicitedNodeAddr(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
protocolFactory stack.TransportProtocol
|
||||
@@ -184,50 +187,61 @@ func TestReceiveOnSolicitedNodeAddr(t *testing.T) {
|
||||
NetworkProtocols: []stack.NetworkProtocol{NewProtocol()},
|
||||
TransportProtocols: []stack.TransportProtocol{test.protocolFactory},
|
||||
})
|
||||
e := channel.New(10, 1280, linkAddr1)
|
||||
if err := s.CreateNIC(1, e); err != nil {
|
||||
t.Fatalf("CreateNIC(_) = %s", err)
|
||||
e := channel.New(1, 1280, linkAddr1)
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
}
|
||||
|
||||
// Should not receive a packet destined to the solicited
|
||||
// node address of addr2/addr3 yet as we haven't added
|
||||
// those addresses.
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
tcpip.Route{
|
||||
Destination: header.IPv6EmptySubnet,
|
||||
NIC: nicID,
|
||||
},
|
||||
})
|
||||
|
||||
// Should not receive a packet destined to the solicited node address of
|
||||
// addr2/addr3 yet as we haven't added those addresses.
|
||||
test.rxf(t, s, e, addr1, snmc, 0)
|
||||
|
||||
if err := s.AddAddress(1, ProtocolNumber, addr2); err != nil {
|
||||
t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, addr2, err)
|
||||
if err := s.AddAddress(nicID, ProtocolNumber, addr2); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, addr2, err)
|
||||
}
|
||||
|
||||
// Should receive a packet destined to the solicited
|
||||
// node address of addr2/addr3 now that we have added
|
||||
// added addr2.
|
||||
// Should receive a packet destined to the solicited node address of
|
||||
// addr2/addr3 now that we have added added addr2.
|
||||
test.rxf(t, s, e, addr1, snmc, 1)
|
||||
|
||||
if err := s.AddAddress(1, ProtocolNumber, addr3); err != nil {
|
||||
t.Fatalf("AddAddress(_, %d, %s) = %s", ProtocolNumber, addr3, err)
|
||||
if err := s.AddAddress(nicID, ProtocolNumber, addr3); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, addr3, err)
|
||||
}
|
||||
|
||||
// Should still receive a packet destined to the
|
||||
// solicited node address of addr2/addr3 now that we
|
||||
// have added addr3.
|
||||
// Should still receive a packet destined to the solicited node address of
|
||||
// addr2/addr3 now that we have added addr3.
|
||||
test.rxf(t, s, e, addr1, snmc, 2)
|
||||
|
||||
if err := s.RemoveAddress(1, addr2); err != nil {
|
||||
t.Fatalf("RemoveAddress(_, %s) = %s", addr2, err)
|
||||
if err := s.RemoveAddress(nicID, addr2); err != nil {
|
||||
t.Fatalf("RemoveAddress(%d, %s) = %s", nicID, addr2, err)
|
||||
}
|
||||
|
||||
// Should still receive a packet destined to the
|
||||
// solicited node address of addr2/addr3 now that we
|
||||
// have removed addr2.
|
||||
// Should still receive a packet destined to the solicited node address of
|
||||
// addr2/addr3 now that we have removed addr2.
|
||||
test.rxf(t, s, e, addr1, snmc, 3)
|
||||
|
||||
if err := s.RemoveAddress(1, addr3); err != nil {
|
||||
t.Fatalf("RemoveAddress(_, %s) = %s", addr3, err)
|
||||
// Make sure addr3's endpoint does not get removed from the NIC by
|
||||
// incrementing its reference count with a route.
|
||||
r, err := s.FindRoute(nicID, addr3, addr4, ProtocolNumber, false)
|
||||
if err != nil {
|
||||
t.Fatalf("FindRoute(%d, %s, %s, %d, false): %s", nicID, addr3, addr4, ProtocolNumber, err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
if err := s.RemoveAddress(nicID, addr3); err != nil {
|
||||
t.Fatalf("RemoveAddress(%d, %s) = %s", nicID, addr3, err)
|
||||
}
|
||||
|
||||
// Should not receive a packet destined to the solicited
|
||||
// node address of addr2/addr3 yet as both of them got
|
||||
// removed.
|
||||
// Should not receive a packet destined to the solicited node address of
|
||||
// addr2/addr3 yet as both of them got removed, even though a route using
|
||||
// addr3 exists.
|
||||
test.rxf(t, s, e, addr1, snmc, 3)
|
||||
})
|
||||
}
|
||||
|
||||
+157
-61
@@ -305,6 +305,15 @@ type NDPConfigurations struct {
|
||||
// lifetime(s) of the generated address changes; this option only
|
||||
// affects the generation of new addresses as part of SLAAC.
|
||||
AutoGenGlobalAddresses bool
|
||||
|
||||
// AutoGenAddressConflictRetries determines how many times to attempt to retry
|
||||
// generation of a permanent auto-generated address in response to DAD
|
||||
// conflicts.
|
||||
//
|
||||
// If the method used to generate the address does not support creating
|
||||
// alternative addresses (e.g. IIDs based on the modified EUI64 of a NIC's
|
||||
// MAC address), then no attempt will be made to resolve the conflict.
|
||||
AutoGenAddressConflictRetries uint8
|
||||
}
|
||||
|
||||
// DefaultNDPConfigurations returns an NDPConfigurations populated with
|
||||
@@ -411,8 +420,23 @@ type slaacPrefixState struct {
|
||||
// Nonzero only when the address is not valid forever.
|
||||
validUntil time.Time
|
||||
|
||||
// Nonzero only when the address is not preferred forever.
|
||||
preferredUntil time.Time
|
||||
|
||||
// The prefix's permanent address endpoint.
|
||||
//
|
||||
// May only be nil when a SLAAC address is being (re-)generated. Otherwise,
|
||||
// must not be nil as all SLAAC prefixes must have a SLAAC address.
|
||||
ref *referencedNetworkEndpoint
|
||||
|
||||
// The number of times a permanent address has been generated for the prefix.
|
||||
//
|
||||
// Addresses may be regenerated in reseponse to a DAD conflicts.
|
||||
generationAttempts uint8
|
||||
|
||||
// The maximum number of times to attempt regeneration of a permanent SLAAC
|
||||
// address in response to DAD conflicts.
|
||||
maxGenerationAttempts uint8
|
||||
}
|
||||
|
||||
// startDuplicateAddressDetection performs Duplicate Address Detection.
|
||||
@@ -935,60 +959,83 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
return
|
||||
}
|
||||
|
||||
// If the preferred lifetime is zero, then the prefix should be considered
|
||||
// deprecated.
|
||||
deprecated := pl == 0
|
||||
ref := ndp.addSLAACAddr(prefix, deprecated)
|
||||
if ref == nil {
|
||||
// We were unable to generate a permanent address for prefix so do nothing
|
||||
// further as there is no reason to maintain state for a SLAAC prefix we
|
||||
// cannot generate a permanent address for.
|
||||
return
|
||||
}
|
||||
|
||||
state := slaacPrefixState{
|
||||
deprecationTimer: tcpip.MakeCancellableTimer(&ndp.nic.mu, func() {
|
||||
prefixState, ok := ndp.slaacPrefixes[prefix]
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the SLAAC prefix %s", prefix))
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the deprecated SLAAC prefix %s", prefix))
|
||||
}
|
||||
|
||||
ndp.deprecateSLAACAddress(prefixState.ref)
|
||||
ndp.deprecateSLAACAddress(state.ref)
|
||||
}),
|
||||
invalidationTimer: tcpip.MakeCancellableTimer(&ndp.nic.mu, func() {
|
||||
ndp.invalidateSLAACPrefix(prefix, true)
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the invalidated SLAAC prefix %s", prefix))
|
||||
}
|
||||
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
}),
|
||||
ref: ref,
|
||||
maxGenerationAttempts: ndp.configs.AutoGenAddressConflictRetries + 1,
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// The time an address is preferred until is needed to properly generate the
|
||||
// address.
|
||||
if pl < header.NDPInfiniteLifetime {
|
||||
state.preferredUntil = now.Add(pl)
|
||||
}
|
||||
|
||||
if !ndp.generateSLAACAddr(prefix, &state) {
|
||||
// We were unable to generate an address for the prefix, we do not nothing
|
||||
// further as there is no reason to maintain state or timers for a prefix we
|
||||
// do not have an address for.
|
||||
return
|
||||
}
|
||||
|
||||
// Setup the initial timers to deprecate and invalidate prefix.
|
||||
|
||||
if !deprecated && pl < header.NDPInfiniteLifetime {
|
||||
if pl < header.NDPInfiniteLifetime && pl != 0 {
|
||||
state.deprecationTimer.Reset(pl)
|
||||
}
|
||||
|
||||
if vl < header.NDPInfiniteLifetime {
|
||||
state.invalidationTimer.Reset(vl)
|
||||
state.validUntil = time.Now().Add(vl)
|
||||
state.validUntil = now.Add(vl)
|
||||
}
|
||||
|
||||
ndp.slaacPrefixes[prefix] = state
|
||||
}
|
||||
|
||||
// addSLAACAddr adds a SLAAC address for prefix.
|
||||
// generateSLAACAddr generates a SLAAC address for prefix.
|
||||
//
|
||||
// Returns true if an address was successfully generated.
|
||||
//
|
||||
// Panics if the prefix is not a SLAAC prefix or it already has an address.
|
||||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) addSLAACAddr(prefix tcpip.Subnet, deprecated bool) *referencedNetworkEndpoint {
|
||||
func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixState) bool {
|
||||
if r := state.ref; r != nil {
|
||||
panic(fmt.Sprintf("ndp: SLAAC prefix %s already has a permenant address %s", prefix, r.addrWithPrefix()))
|
||||
}
|
||||
|
||||
// If we have already reached the maximum address generation attempts for the
|
||||
// prefix, do not generate another address.
|
||||
if state.generationAttempts == state.maxGenerationAttempts {
|
||||
return false
|
||||
}
|
||||
|
||||
addrBytes := []byte(prefix.ID())
|
||||
if oIID := ndp.nic.stack.opaqueIIDOpts; oIID.NICNameFromID != nil {
|
||||
addrBytes = header.AppendOpaqueInterfaceIdentifier(
|
||||
addrBytes[:header.IIDOffsetInIPv6Address],
|
||||
prefix,
|
||||
oIID.NICNameFromID(ndp.nic.ID(), ndp.nic.name),
|
||||
0, /* dadCounter */
|
||||
state.generationAttempts,
|
||||
oIID.SecretKey,
|
||||
)
|
||||
} else {
|
||||
} else if state.generationAttempts == 0 {
|
||||
// Only attempt to generate an interface-specific IID if we have a valid
|
||||
// link address.
|
||||
//
|
||||
@@ -996,12 +1043,16 @@ func (ndp *ndpState) addSLAACAddr(prefix tcpip.Subnet, deprecated bool) *referen
|
||||
// LinkEndpoint.LinkAddress) before reaching this point.
|
||||
linkAddr := ndp.nic.linkEP.LinkAddress()
|
||||
if !header.IsValidUnicastEthernetAddress(linkAddr) {
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Generate an address within prefix from the modified EUI-64 of ndp's NIC's
|
||||
// Ethernet MAC address.
|
||||
header.EthernetAdddressToModifiedEUI64IntoBuf(linkAddr, addrBytes[header.IIDOffsetInIPv6Address:])
|
||||
} else {
|
||||
// We have no way to regenerate an address when addresses are not generated
|
||||
// with opaque IIDs.
|
||||
return false
|
||||
}
|
||||
|
||||
generatedAddr := tcpip.ProtocolAddress{
|
||||
@@ -1014,26 +1065,52 @@ func (ndp *ndpState) addSLAACAddr(prefix tcpip.Subnet, deprecated bool) *referen
|
||||
|
||||
// If the nic already has this address, do nothing further.
|
||||
if ndp.nic.hasPermanentAddrLocked(generatedAddr.AddressWithPrefix.Address) {
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Inform the integrator that we have a new SLAAC address.
|
||||
ndpDisp := ndp.nic.stack.ndpDisp
|
||||
if ndpDisp == nil {
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
if !ndpDisp.OnAutoGenAddress(ndp.nic.ID(), generatedAddr.AddressWithPrefix) {
|
||||
// Informed by the integrator not to add the address.
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
deprecated := time.Since(state.preferredUntil) >= 0
|
||||
ref, err := ndp.nic.addAddressLocked(generatedAddr, FirstPrimaryEndpoint, permanent, slaac, deprecated)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ndp: error when adding address %+v: %s", generatedAddr, err))
|
||||
}
|
||||
|
||||
return ref
|
||||
state.generationAttempts++
|
||||
state.ref = ref
|
||||
return true
|
||||
}
|
||||
|
||||
// regenerateSLAACAddr regenerates an address for a SLAAC prefix.
|
||||
//
|
||||
// If generating a new address for the prefix fails, the prefix will be
|
||||
// invalidated.
|
||||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) regenerateSLAACAddr(prefix tcpip.Subnet) {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: SLAAC prefix state not found to regenerate address for %s", prefix))
|
||||
}
|
||||
|
||||
if ndp.generateSLAACAddr(prefix, &state) {
|
||||
ndp.slaacPrefixes[prefix] = state
|
||||
return
|
||||
}
|
||||
|
||||
// We were unable to generate a permanent address for the SLAAC prefix so
|
||||
// invalidate the prefix as there is no reason to maintain state for a
|
||||
// SLAAC prefix we do not have an address for.
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
}
|
||||
|
||||
// refreshSLAACPrefixLifetimes refreshes the lifetimes of a SLAAC prefix.
|
||||
@@ -1060,9 +1137,16 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, pl, vl tim
|
||||
// deprecation timer so it can be reset.
|
||||
prefixState.deprecationTimer.StopLocked()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Reset the deprecation timer if prefix has a finite preferred lifetime.
|
||||
if !deprecated && pl < header.NDPInfiniteLifetime {
|
||||
prefixState.deprecationTimer.Reset(pl)
|
||||
if pl < header.NDPInfiniteLifetime {
|
||||
if !deprecated {
|
||||
prefixState.deprecationTimer.Reset(pl)
|
||||
}
|
||||
prefixState.preferredUntil = now.Add(pl)
|
||||
} else {
|
||||
prefixState.preferredUntil = time.Time{}
|
||||
}
|
||||
|
||||
// As per RFC 4862 section 5.5.3.e, update the valid lifetime for prefix:
|
||||
@@ -1105,7 +1189,7 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, pl, vl tim
|
||||
|
||||
prefixState.invalidationTimer.StopLocked()
|
||||
prefixState.invalidationTimer.Reset(effectiveVl)
|
||||
prefixState.validUntil = time.Now().Add(effectiveVl)
|
||||
prefixState.validUntil = now.Add(effectiveVl)
|
||||
}
|
||||
|
||||
// deprecateSLAACAddress marks ref as deprecated and notifies the stack's NDP
|
||||
@@ -1121,48 +1205,60 @@ func (ndp *ndpState) deprecateSLAACAddress(ref *referencedNetworkEndpoint) {
|
||||
|
||||
ref.deprecated = true
|
||||
if ndpDisp := ndp.nic.stack.ndpDisp; ndpDisp != nil {
|
||||
ndpDisp.OnAutoGenAddressDeprecated(ndp.nic.ID(), tcpip.AddressWithPrefix{
|
||||
Address: ref.ep.ID().LocalAddress,
|
||||
PrefixLen: ref.ep.PrefixLen(),
|
||||
})
|
||||
ndpDisp.OnAutoGenAddressDeprecated(ndp.nic.ID(), ref.addrWithPrefix())
|
||||
}
|
||||
}
|
||||
|
||||
// invalidateSLAACPrefix invalidates a SLAAC prefix.
|
||||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, removeAddr bool) {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
state.deprecationTimer.StopLocked()
|
||||
state.invalidationTimer.StopLocked()
|
||||
delete(ndp.slaacPrefixes, prefix)
|
||||
|
||||
addr := state.ref.ep.ID().LocalAddress
|
||||
|
||||
if removeAddr {
|
||||
if err := ndp.nic.removePermanentAddressLocked(addr); err != nil {
|
||||
panic(fmt.Sprintf("ndp: removePermanentAddressLocked(%s): %s", addr, err))
|
||||
func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, state slaacPrefixState) {
|
||||
if r := state.ref; r != nil {
|
||||
// Since we are already invalidating the prefix, do not invalidate the
|
||||
// prefix when removing the address.
|
||||
if err := ndp.nic.removePermanentIPv6EndpointLocked(r, false /* allowSLAACPrefixInvalidation */); err != nil {
|
||||
panic(fmt.Sprintf("ndp: removePermanentIPv6EndpointLocked(%s, false): %s", r.addrWithPrefix(), err))
|
||||
}
|
||||
}
|
||||
|
||||
if ndpDisp := ndp.nic.stack.ndpDisp; ndpDisp != nil {
|
||||
ndpDisp.OnAutoGenAddressInvalidated(ndp.nic.ID(), tcpip.AddressWithPrefix{
|
||||
Address: addr,
|
||||
PrefixLen: state.ref.ep.PrefixLen(),
|
||||
})
|
||||
}
|
||||
ndp.cleanupSLAACPrefixResources(prefix, state)
|
||||
}
|
||||
|
||||
// cleanupSLAACAddrResourcesAndNotify cleans up an invalidated SLAAC
|
||||
// address's resources from ndp.
|
||||
// cleanupSLAACAddrResourcesAndNotify cleans up an invalidated SLAAC address's
|
||||
// resources.
|
||||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) cleanupSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPrefix) {
|
||||
ndp.invalidateSLAACPrefix(addr.Subnet(), false)
|
||||
func (ndp *ndpState) cleanupSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPrefix, invalidatePrefix bool) {
|
||||
if ndpDisp := ndp.nic.stack.ndpDisp; ndpDisp != nil {
|
||||
ndpDisp.OnAutoGenAddressInvalidated(ndp.nic.ID(), addr)
|
||||
}
|
||||
|
||||
prefix := addr.Subnet()
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok || state.ref == nil || addr.Address != state.ref.ep.ID().LocalAddress {
|
||||
return
|
||||
}
|
||||
|
||||
if !invalidatePrefix {
|
||||
// If the prefix is not being invalidated, disassociate the address from the
|
||||
// prefix and do nothing further.
|
||||
state.ref = nil
|
||||
ndp.slaacPrefixes[prefix] = state
|
||||
return
|
||||
}
|
||||
|
||||
ndp.cleanupSLAACPrefixResources(prefix, state)
|
||||
}
|
||||
|
||||
// cleanupSLAACPrefixResources cleansup a SLAAC prefix's timers and entry.
|
||||
//
|
||||
// Panics if the SLAAC prefix is not known.
|
||||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) cleanupSLAACPrefixResources(prefix tcpip.Subnet, state slaacPrefixState) {
|
||||
state.deprecationTimer.StopLocked()
|
||||
state.invalidationTimer.StopLocked()
|
||||
delete(ndp.slaacPrefixes, prefix)
|
||||
}
|
||||
|
||||
// cleanupState cleans up ndp's state.
|
||||
@@ -1181,7 +1277,7 @@ func (ndp *ndpState) cleanupSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPr
|
||||
func (ndp *ndpState) cleanupState(hostOnly bool) {
|
||||
linkLocalSubnet := header.IPv6LinkLocalPrefix.Subnet()
|
||||
linkLocalPrefixes := 0
|
||||
for prefix := range ndp.slaacPrefixes {
|
||||
for prefix, state := range ndp.slaacPrefixes {
|
||||
// RFC 4862 section 5 states that routers are also expected to generate a
|
||||
// link-local address so we do not invalidate them if we are cleaning up
|
||||
// host-only state.
|
||||
@@ -1190,7 +1286,7 @@ func (ndp *ndpState) cleanupState(hostOnly bool) {
|
||||
continue
|
||||
}
|
||||
|
||||
ndp.invalidateSLAACPrefix(prefix, true)
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
}
|
||||
|
||||
if got := len(ndp.slaacPrefixes); got != linkLocalPrefixes {
|
||||
|
||||
@@ -623,6 +623,12 @@ func TestDADFail(t *testing.T) {
|
||||
if want := (tcpip.AddressWithPrefix{}); addr != want {
|
||||
t.Fatalf("got stack.GetMainNICAddress(%d, %d) = (%s, nil), want = (%s, nil)", nicID, header.IPv6ProtocolNumber, addr, want)
|
||||
}
|
||||
|
||||
// Attempting to add the address again should not fail if the address's
|
||||
// state was cleaned up when DAD failed.
|
||||
if err := s.AddAddress(nicID, header.IPv6ProtocolNumber, addr1); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv6ProtocolNumber, addr1, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2783,6 +2789,461 @@ func TestAutoGenAddrWithOpaqueIID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestAutoGenAddrWithOpaqueIIDDADRetries tests the regeneration of an
|
||||
// auto-generated IPv6 address in response to a DAD conflict.
|
||||
func TestAutoGenAddrWithOpaqueIIDDADRetries(t *testing.T) {
|
||||
const nicID = 1
|
||||
const nicName = "nic"
|
||||
const dadTransmits = 1
|
||||
const retransmitTimer = time.Second
|
||||
const maxMaxRetries = 3
|
||||
const lifetimeSeconds = 10
|
||||
|
||||
var secretKeyBuf [header.OpaqueIIDSecretKeyMinBytes]byte
|
||||
secretKey := secretKeyBuf[:]
|
||||
n, err := rand.Read(secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("rand.Read(_): %s", err)
|
||||
}
|
||||
if n != header.OpaqueIIDSecretKeyMinBytes {
|
||||
t.Fatalf("got rand.Read(_) = (%d, _), want = (%d, _)", n, header.OpaqueIIDSecretKeyMinBytes)
|
||||
}
|
||||
|
||||
prefix, subnet, _ := prefixSubnetAddr(0, linkAddr1)
|
||||
|
||||
for maxRetries := uint8(0); maxRetries <= maxMaxRetries; maxRetries++ {
|
||||
for numFailures := uint8(0); numFailures <= maxRetries+1; numFailures++ {
|
||||
addrTypes := []struct {
|
||||
name string
|
||||
ndpConfigs stack.NDPConfigurations
|
||||
autoGenLinkLocal bool
|
||||
subnet tcpip.Subnet
|
||||
triggerSLAACFn func(e *channel.Endpoint)
|
||||
}{
|
||||
{
|
||||
name: "Global address",
|
||||
ndpConfigs: stack.NDPConfigurations{
|
||||
DupAddrDetectTransmits: dadTransmits,
|
||||
RetransmitTimer: retransmitTimer,
|
||||
HandleRAs: true,
|
||||
AutoGenGlobalAddresses: true,
|
||||
AutoGenAddressConflictRetries: maxRetries,
|
||||
},
|
||||
subnet: subnet,
|
||||
triggerSLAACFn: func(e *channel.Endpoint) {
|
||||
// Receive an RA with prefix1 in a PI.
|
||||
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, lifetimeSeconds, lifetimeSeconds))
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LinkLocal address",
|
||||
ndpConfigs: stack.NDPConfigurations{
|
||||
DupAddrDetectTransmits: dadTransmits,
|
||||
RetransmitTimer: retransmitTimer,
|
||||
AutoGenAddressConflictRetries: maxRetries,
|
||||
},
|
||||
autoGenLinkLocal: true,
|
||||
subnet: header.IPv6LinkLocalPrefix.Subnet(),
|
||||
triggerSLAACFn: func(e *channel.Endpoint) {},
|
||||
},
|
||||
}
|
||||
|
||||
for _, addrType := range addrTypes {
|
||||
maxRetries := maxRetries
|
||||
numFailures := numFailures
|
||||
addrType := addrType
|
||||
|
||||
t.Run(fmt.Sprintf("%s with %d max retries and %d failures", addrType.name, maxRetries, numFailures), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent, 1),
|
||||
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
|
||||
}
|
||||
e := channel.New(0, 1280, linkAddr1)
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
AutoGenIPv6LinkLocal: addrType.autoGenLinkLocal,
|
||||
NDPConfigs: addrType.ndpConfigs,
|
||||
NDPDisp: &ndpDisp,
|
||||
OpaqueIIDOpts: stack.OpaqueInterfaceIdentifierOptions{
|
||||
NICNameFromID: func(_ tcpip.NICID, nicName string) string {
|
||||
return nicName
|
||||
},
|
||||
SecretKey: secretKey,
|
||||
},
|
||||
})
|
||||
opts := stack.NICOptions{Name: nicName}
|
||||
if err := s.CreateNICWithOptions(nicID, e, opts); err != nil {
|
||||
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, opts, err)
|
||||
}
|
||||
|
||||
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
|
||||
t.Helper()
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
|
||||
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected addr auto gen event")
|
||||
}
|
||||
}
|
||||
|
||||
addrType.triggerSLAACFn(e)
|
||||
|
||||
// Simulate DAD conflicts so the address is regenerated.
|
||||
for i := uint8(0); i < numFailures; i++ {
|
||||
addrBytes := []byte(addrType.subnet.ID())
|
||||
addr := tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], addrType.subnet, nicName, i, secretKey)),
|
||||
PrefixLen: 64,
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, newAddr)
|
||||
|
||||
// Should not have any addresses assigned to the NIC.
|
||||
mainAddr, err := s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("stack.GetMainNICAddress(%d, _) err = %s", nicID, err)
|
||||
}
|
||||
if want := (tcpip.AddressWithPrefix{}); mainAddr != want {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", mainAddr, want)
|
||||
}
|
||||
|
||||
// Simulate a DAD conflict.
|
||||
if err := s.DupTentativeAddrDetected(nicID, addr.Address); err != nil {
|
||||
t.Fatalf("s.DupTentativeAddrDetected(%d, %s): %s", nicID, addr.Address, err)
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, invalidatedAddr)
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr.Address, false, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected DAD event")
|
||||
}
|
||||
|
||||
// Attempting to add the address manually should not fail if the
|
||||
// address's state was cleaned up when DAD failed.
|
||||
if err := s.AddAddress(nicID, header.IPv6ProtocolNumber, addr.Address); err != nil {
|
||||
t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, header.IPv6ProtocolNumber, addr.Address, err)
|
||||
}
|
||||
if err := s.RemoveAddress(nicID, addr.Address); err != nil {
|
||||
t.Fatalf("RemoveAddress(%d, %s) = %s", nicID, addr.Address, err)
|
||||
}
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr.Address, false, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected DAD event")
|
||||
}
|
||||
}
|
||||
|
||||
// Should not have any addresses assigned to the NIC.
|
||||
mainAddr, err := s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("stack.GetMainNICAddress(%d, _) err = %s", nicID, err)
|
||||
}
|
||||
if want := (tcpip.AddressWithPrefix{}); mainAddr != want {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", mainAddr, want)
|
||||
}
|
||||
|
||||
// If we had less failures than generation attempts, we should have an
|
||||
// address after DAD resolves.
|
||||
if maxRetries+1 > numFailures {
|
||||
addrBytes := []byte(addrType.subnet.ID())
|
||||
addr := tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], addrType.subnet, nicName, numFailures, secretKey)),
|
||||
PrefixLen: 64,
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, newAddr)
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr.Address, true, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
case <-time.After(dadTransmits*retransmitTimer + defaultAsyncEventTimeout):
|
||||
t.Fatal("timed out waiting for DAD event")
|
||||
}
|
||||
|
||||
mainAddr, err := s.GetMainNICAddress(nicID, header.IPv6ProtocolNumber)
|
||||
if err != nil {
|
||||
t.Fatalf("stack.GetMainNICAddress(%d, _) err = %s", nicID, err)
|
||||
}
|
||||
if mainAddr != addr {
|
||||
t.Fatalf("got stack.GetMainNICAddress(_, _) = (%s, nil), want = (%s, nil)", mainAddr, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Should not attempt address regeneration again.
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
t.Fatalf("unexpectedly got an auto-generated address event = %+v", e)
|
||||
case <-time.After(defaultAsyncEventTimeout):
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAutoGenAddrWithEUI64IIDNoDADRetries tests that a regeneration attempt is
|
||||
// not made for SLAAC addresses generated with an IID based on the NIC's link
|
||||
// address.
|
||||
func TestAutoGenAddrWithEUI64IIDNoDADRetries(t *testing.T) {
|
||||
const nicID = 1
|
||||
const dadTransmits = 1
|
||||
const retransmitTimer = time.Second
|
||||
const maxRetries = 3
|
||||
const lifetimeSeconds = 10
|
||||
|
||||
prefix, subnet, _ := prefixSubnetAddr(0, linkAddr1)
|
||||
|
||||
addrTypes := []struct {
|
||||
name string
|
||||
ndpConfigs stack.NDPConfigurations
|
||||
autoGenLinkLocal bool
|
||||
subnet tcpip.Subnet
|
||||
triggerSLAACFn func(e *channel.Endpoint)
|
||||
}{
|
||||
{
|
||||
name: "Global address",
|
||||
ndpConfigs: stack.NDPConfigurations{
|
||||
DupAddrDetectTransmits: dadTransmits,
|
||||
RetransmitTimer: retransmitTimer,
|
||||
HandleRAs: true,
|
||||
AutoGenGlobalAddresses: true,
|
||||
AutoGenAddressConflictRetries: maxRetries,
|
||||
},
|
||||
subnet: subnet,
|
||||
triggerSLAACFn: func(e *channel.Endpoint) {
|
||||
// Receive an RA with prefix1 in a PI.
|
||||
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, lifetimeSeconds, lifetimeSeconds))
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LinkLocal address",
|
||||
ndpConfigs: stack.NDPConfigurations{
|
||||
DupAddrDetectTransmits: dadTransmits,
|
||||
RetransmitTimer: retransmitTimer,
|
||||
AutoGenAddressConflictRetries: maxRetries,
|
||||
},
|
||||
autoGenLinkLocal: true,
|
||||
subnet: header.IPv6LinkLocalPrefix.Subnet(),
|
||||
triggerSLAACFn: func(e *channel.Endpoint) {},
|
||||
},
|
||||
}
|
||||
|
||||
for _, addrType := range addrTypes {
|
||||
addrType := addrType
|
||||
|
||||
t.Run(addrType.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent, 1),
|
||||
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
|
||||
}
|
||||
e := channel.New(0, 1280, linkAddr1)
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
AutoGenIPv6LinkLocal: addrType.autoGenLinkLocal,
|
||||
NDPConfigs: addrType.ndpConfigs,
|
||||
NDPDisp: &ndpDisp,
|
||||
})
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
}
|
||||
|
||||
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
|
||||
t.Helper()
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
|
||||
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected addr auto gen event")
|
||||
}
|
||||
}
|
||||
|
||||
addrType.triggerSLAACFn(e)
|
||||
|
||||
addrBytes := []byte(addrType.subnet.ID())
|
||||
header.EthernetAdddressToModifiedEUI64IntoBuf(linkAddr1, addrBytes[header.IIDOffsetInIPv6Address:])
|
||||
addr := tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(addrBytes),
|
||||
PrefixLen: 64,
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, newAddr)
|
||||
|
||||
// Simulate a DAD conflict.
|
||||
if err := s.DupTentativeAddrDetected(nicID, addr.Address); err != nil {
|
||||
t.Fatalf("s.DupTentativeAddrDetected(%d, %s): %s", nicID, addr.Address, err)
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, invalidatedAddr)
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr.Address, false, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected DAD event")
|
||||
}
|
||||
|
||||
// Should not attempt address regeneration.
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
t.Fatalf("unexpectedly got an auto-generated address event = %+v", e)
|
||||
case <-time.After(defaultAsyncEventTimeout):
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAutoGenAddrContinuesLifetimesAfterRetry tests that retrying address
|
||||
// generation in response to DAD conflicts does not refresh the lifetimes.
|
||||
func TestAutoGenAddrContinuesLifetimesAfterRetry(t *testing.T) {
|
||||
const nicID = 1
|
||||
const nicName = "nic"
|
||||
const dadTransmits = 1
|
||||
const retransmitTimer = 2 * time.Second
|
||||
const failureTimer = time.Second
|
||||
const maxRetries = 1
|
||||
const lifetimeSeconds = 5
|
||||
|
||||
var secretKeyBuf [header.OpaqueIIDSecretKeyMinBytes]byte
|
||||
secretKey := secretKeyBuf[:]
|
||||
n, err := rand.Read(secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("rand.Read(_): %s", err)
|
||||
}
|
||||
if n != header.OpaqueIIDSecretKeyMinBytes {
|
||||
t.Fatalf("got rand.Read(_) = (%d, _), want = (%d, _)", n, header.OpaqueIIDSecretKeyMinBytes)
|
||||
}
|
||||
|
||||
prefix, subnet, _ := prefixSubnetAddr(0, linkAddr1)
|
||||
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent, 1),
|
||||
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
|
||||
}
|
||||
e := channel.New(0, 1280, linkAddr1)
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
NDPConfigs: stack.NDPConfigurations{
|
||||
DupAddrDetectTransmits: dadTransmits,
|
||||
RetransmitTimer: retransmitTimer,
|
||||
HandleRAs: true,
|
||||
AutoGenGlobalAddresses: true,
|
||||
AutoGenAddressConflictRetries: maxRetries,
|
||||
},
|
||||
NDPDisp: &ndpDisp,
|
||||
OpaqueIIDOpts: stack.OpaqueInterfaceIdentifierOptions{
|
||||
NICNameFromID: func(_ tcpip.NICID, nicName string) string {
|
||||
return nicName
|
||||
},
|
||||
SecretKey: secretKey,
|
||||
},
|
||||
})
|
||||
opts := stack.NICOptions{Name: nicName}
|
||||
if err := s.CreateNICWithOptions(nicID, e, opts); err != nil {
|
||||
t.Fatalf("CreateNICWithOptions(%d, _, %+v) = %s", nicID, opts, err)
|
||||
}
|
||||
|
||||
expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) {
|
||||
t.Helper()
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" {
|
||||
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected addr auto gen event")
|
||||
}
|
||||
}
|
||||
|
||||
// Receive an RA with prefix in a PI.
|
||||
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, lifetimeSeconds, lifetimeSeconds))
|
||||
|
||||
addrBytes := []byte(subnet.ID())
|
||||
addr := tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet, nicName, 0, secretKey)),
|
||||
PrefixLen: 64,
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, newAddr)
|
||||
|
||||
// Simulate a DAD conflict after some time has passed.
|
||||
time.Sleep(failureTimer)
|
||||
if err := s.DupTentativeAddrDetected(nicID, addr.Address); err != nil {
|
||||
t.Fatalf("s.DupTentativeAddrDetected(%d, %s): %s", nicID, addr.Address, err)
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, invalidatedAddr)
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr.Address, false, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
default:
|
||||
t.Fatal("expected DAD event")
|
||||
}
|
||||
|
||||
// Let the next address resolve.
|
||||
addr.Address = tcpip.Address(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet, nicName, 1, secretKey))
|
||||
expectAutoGenAddrEvent(addr, newAddr)
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr.Address, true, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
case <-time.After(dadTransmits*retransmitTimer + defaultAsyncEventTimeout):
|
||||
t.Fatal("timed out waiting for DAD event")
|
||||
}
|
||||
|
||||
// Address should be deprecated/invalidated after the lifetime expires.
|
||||
//
|
||||
// Note, the remaining lifetime is calculated from when the PI was first
|
||||
// processed. Since we wait for some time before simulating a DAD conflict
|
||||
// and more time for the new address to resolve, the new address is only
|
||||
// expected to be valid for the remaining time. The DAD conflict should
|
||||
// not have reset the lifetimes.
|
||||
//
|
||||
// We expect either just the invalidation event or the deprecation event
|
||||
// followed by the invalidation event.
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
if e.eventType == deprecatedAddr {
|
||||
if diff := checkAutoGenAddrEvent(e, addr, deprecatedAddr); diff != "" {
|
||||
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
|
||||
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
case <-time.After(defaultAsyncEventTimeout):
|
||||
t.Fatal("timed out waiting for invalidated auto gen addr event after deprecation")
|
||||
}
|
||||
} else {
|
||||
if diff := checkAutoGenAddrEvent(e, addr, invalidatedAddr); diff != "" {
|
||||
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
case <-time.After(lifetimeSeconds*time.Second - failureTimer - dadTransmits*retransmitTimer + defaultAsyncEventTimeout):
|
||||
t.Fatal("timed out waiting for auto gen addr event")
|
||||
}
|
||||
}
|
||||
|
||||
// TestNDPRecursiveDNSServerDispatch tests that we properly dispatch an event
|
||||
// to the integrator when an RA is received with the NDP Recursive DNS Server
|
||||
// option with at least one valid address.
|
||||
|
||||
+49
-24
@@ -1012,29 +1012,31 @@ func (n *NIC) removePermanentAddressLocked(addr tcpip.Address) *tcpip.Error {
|
||||
return tcpip.ErrBadLocalAddress
|
||||
}
|
||||
|
||||
isIPv6Unicast := r.protocol == header.IPv6ProtocolNumber && header.IsV6UnicastAddress(addr)
|
||||
switch r.protocol {
|
||||
case header.IPv6ProtocolNumber:
|
||||
return n.removePermanentIPv6EndpointLocked(r, true /* allowSLAAPrefixInvalidation */)
|
||||
default:
|
||||
r.expireLocked()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) removePermanentIPv6EndpointLocked(r *referencedNetworkEndpoint, allowSLAACPrefixInvalidation bool) *tcpip.Error {
|
||||
addr := r.addrWithPrefix()
|
||||
|
||||
isIPv6Unicast := header.IsV6UnicastAddress(addr.Address)
|
||||
|
||||
if isIPv6Unicast {
|
||||
// If we are removing a tentative IPv6 unicast address, stop DAD.
|
||||
if kind == permanentTentative {
|
||||
n.mu.ndp.stopDuplicateAddressDetection(addr)
|
||||
}
|
||||
n.mu.ndp.stopDuplicateAddressDetection(addr.Address)
|
||||
|
||||
// If we are removing an address generated via SLAAC, cleanup
|
||||
// its SLAAC resources and notify the integrator.
|
||||
if r.configType == slaac {
|
||||
n.mu.ndp.cleanupSLAACAddrResourcesAndNotify(tcpip.AddressWithPrefix{
|
||||
Address: addr,
|
||||
PrefixLen: r.ep.PrefixLen(),
|
||||
})
|
||||
n.mu.ndp.cleanupSLAACAddrResourcesAndNotify(addr, allowSLAACPrefixInvalidation)
|
||||
}
|
||||
}
|
||||
|
||||
r.setKind(permanentExpired)
|
||||
if !r.decRefLocked() {
|
||||
// The endpoint still has references to it.
|
||||
return nil
|
||||
}
|
||||
r.expireLocked()
|
||||
|
||||
// At this point the endpoint is deleted.
|
||||
|
||||
@@ -1044,7 +1046,7 @@ func (n *NIC) removePermanentAddressLocked(addr tcpip.Address) *tcpip.Error {
|
||||
// We ignore the tcpip.ErrBadLocalAddress error because the solicited-node
|
||||
// multicast group may be left by user action.
|
||||
if isIPv6Unicast {
|
||||
snmc := header.SolicitedNodeAddr(addr)
|
||||
snmc := header.SolicitedNodeAddr(addr.Address)
|
||||
if err := n.leaveGroupLocked(snmc, false /* force */); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
return err
|
||||
}
|
||||
@@ -1425,10 +1427,12 @@ func (n *NIC) isAddrTentative(addr tcpip.Address) bool {
|
||||
return ref.getKind() == permanentTentative
|
||||
}
|
||||
|
||||
// dupTentativeAddrDetected attempts to inform n that a tentative addr
|
||||
// is a duplicate on a link.
|
||||
// dupTentativeAddrDetected attempts to inform n that a tentative addr is a
|
||||
// duplicate on a link.
|
||||
//
|
||||
// dupTentativeAddrDetected will delete the tentative address if it exists.
|
||||
// dupTentativeAddrDetected will remove the tentative address if it exists. If
|
||||
// the address was generated via SLAAC, an attempt will be made to generate a
|
||||
// new address.
|
||||
func (n *NIC) dupTentativeAddrDetected(addr tcpip.Address) *tcpip.Error {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
@@ -1442,7 +1446,17 @@ func (n *NIC) dupTentativeAddrDetected(addr tcpip.Address) *tcpip.Error {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
return n.removePermanentAddressLocked(addr)
|
||||
// If the address is a SLAAC address, do not invalidate its SLAAC prefix as a
|
||||
// new address will be generated for it.
|
||||
if err := n.removePermanentIPv6EndpointLocked(ref, false /* allowSLAACPrefixInvalidation */); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ref.configType == slaac {
|
||||
n.mu.ndp.regenerateSLAACAddr(ref.addrWithPrefix().Subnet())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setNDPConfigs sets the NDP configurations for n.
|
||||
@@ -1570,6 +1584,13 @@ type referencedNetworkEndpoint struct {
|
||||
deprecated bool
|
||||
}
|
||||
|
||||
func (r *referencedNetworkEndpoint) addrWithPrefix() tcpip.AddressWithPrefix {
|
||||
return tcpip.AddressWithPrefix{
|
||||
Address: r.ep.ID().LocalAddress,
|
||||
PrefixLen: r.ep.PrefixLen(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *referencedNetworkEndpoint) getKind() networkEndpointKind {
|
||||
return networkEndpointKind(atomic.LoadInt32((*int32)(&r.kind)))
|
||||
}
|
||||
@@ -1597,6 +1618,13 @@ func (r *referencedNetworkEndpoint) isValidForOutgoingRLocked() bool {
|
||||
return r.nic.mu.enabled && (r.getKind() != permanentExpired || r.nic.mu.spoofing)
|
||||
}
|
||||
|
||||
// expireLocked decrements the reference count and marks the permanent endpoint
|
||||
// as expired.
|
||||
func (r *referencedNetworkEndpoint) expireLocked() {
|
||||
r.setKind(permanentExpired)
|
||||
r.decRefLocked()
|
||||
}
|
||||
|
||||
// decRef decrements the ref count and cleans up the endpoint once it reaches
|
||||
// zero.
|
||||
func (r *referencedNetworkEndpoint) decRef() {
|
||||
@@ -1606,14 +1634,11 @@ func (r *referencedNetworkEndpoint) decRef() {
|
||||
}
|
||||
|
||||
// decRefLocked is the same as decRef but assumes that the NIC.mu mutex is
|
||||
// locked. Returns true if the endpoint was removed.
|
||||
func (r *referencedNetworkEndpoint) decRefLocked() bool {
|
||||
// locked.
|
||||
func (r *referencedNetworkEndpoint) decRefLocked() {
|
||||
if atomic.AddInt32(&r.refs, -1) == 0 {
|
||||
r.nic.removeEndpointLocked(r)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// incRef increments the ref count. It must only be called when the caller is
|
||||
|
||||
Reference in New Issue
Block a user