Regenerate SLAAC address on conflicts with the NIC
If the NIC already has a generated SLAAC address, regenerate a new SLAAC address until one is generated that does not conflict with the NIC's existing addresses, up to a maximum of 10 attempts. This applies to both stable and temporary SLAAC addresses. Test: stack_test.TestMixedSLAACAddrConflictRegen PiperOrigin-RevId: 309495628
This commit is contained in:
parent
5e1e61fbcb
commit
40d6aae122
|
@ -145,6 +145,10 @@ const (
|
|||
// minRegenAdvanceDuration is the minimum duration before the deprecation
|
||||
// of a temporary address when a new address will be generated.
|
||||
minRegenAdvanceDuration = time.Duration(0)
|
||||
|
||||
// maxSLAACAddrLocalRegenAttempts is the maximum number of times to attempt
|
||||
// SLAAC address regenerations in response to a NIC-local conflict.
|
||||
maxSLAACAddrLocalRegenAttempts = 10
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -565,11 +569,18 @@ type slaacPrefixState struct {
|
|||
// Nonzero only when the address is not preferred forever.
|
||||
preferredUntil time.Time
|
||||
|
||||
// The endpoint for the stable address generated for a SLAAC prefix.
|
||||
// State associated with the stable address generated for the prefix.
|
||||
stableAddr struct {
|
||||
// The address's endpoint.
|
||||
//
|
||||
// May only be nil when a SLAAC address is being (re-)generated. Otherwise,
|
||||
// May only be nil when the address is being (re-)generated. Otherwise,
|
||||
// must not be nil as all SLAAC prefixes must have a stable address.
|
||||
stableAddrRef *referencedNetworkEndpoint
|
||||
ref *referencedNetworkEndpoint
|
||||
|
||||
// The number of times an address has been generated locally where the NIC
|
||||
// already had the generated address.
|
||||
localGenerationFailures uint8
|
||||
}
|
||||
|
||||
// The temporary (short-lived) addresses generated for the SLAAC prefix.
|
||||
tempAddrs map[tcpip.Address]tempSLAACAddrState
|
||||
|
@ -579,7 +590,7 @@ type slaacPrefixState struct {
|
|||
// in the generation and DAD process at any time. That is, no two addresses
|
||||
// will be generated at the same time for a given SLAAC prefix.
|
||||
|
||||
// The number of times an address has been generated.
|
||||
// The number of times an address has been generated and added to the NIC.
|
||||
//
|
||||
// Addresses may be regenerated in reseponse to a DAD conflicts.
|
||||
generationAttempts uint8
|
||||
|
@ -1125,7 +1136,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
|||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the deprecated SLAAC prefix %s", prefix))
|
||||
}
|
||||
|
||||
ndp.deprecateSLAACAddress(state.stableAddrRef)
|
||||
ndp.deprecateSLAACAddress(state.stableAddr.ref)
|
||||
}),
|
||||
invalidationTimer: tcpip.NewCancellableTimer(&ndp.nic.mu, func() {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
|
@ -1166,7 +1177,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
|||
}
|
||||
|
||||
// If the address is assigned (DAD resolved), generate a temporary address.
|
||||
if state.stableAddrRef.getKind() == permanent {
|
||||
if state.stableAddr.ref.getKind() == permanent {
|
||||
// Reset the generation attempts counter as we are starting the generation
|
||||
// of a new address for the SLAAC prefix.
|
||||
ndp.generateTempSLAACAddr(prefix, &state, true /* resetGenAttempts */)
|
||||
|
@ -1179,11 +1190,6 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
|||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) addSLAACAddr(addr tcpip.AddressWithPrefix, configType networkEndpointConfigType, deprecated bool) *referencedNetworkEndpoint {
|
||||
// If the nic already has this address, do nothing further.
|
||||
if ndp.nic.hasPermanentAddrLocked(addr.Address) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Inform the integrator that we have a new SLAAC address.
|
||||
ndpDisp := ndp.nic.stack.ndpDisp
|
||||
if ndpDisp == nil {
|
||||
|
@ -1216,7 +1222,7 @@ func (ndp *ndpState) addSLAACAddr(addr tcpip.AddressWithPrefix, configType netwo
|
|||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixState) bool {
|
||||
if r := state.stableAddrRef; r != nil {
|
||||
if r := state.stableAddr.ref; r != nil {
|
||||
panic(fmt.Sprintf("ndp: SLAAC prefix %s already has a permenant address %s", prefix, r.addrWithPrefix()))
|
||||
}
|
||||
|
||||
|
@ -1226,16 +1232,29 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
|
|||
return false
|
||||
}
|
||||
|
||||
var generatedAddr tcpip.AddressWithPrefix
|
||||
addrBytes := []byte(prefix.ID())
|
||||
|
||||
for i := 0; ; i++ {
|
||||
// If we were unable to generate an address after the maximum SLAAC address
|
||||
// local regeneration attempts, do nothing further.
|
||||
if i == maxSLAACAddrLocalRegenAttempts {
|
||||
return false
|
||||
}
|
||||
|
||||
dadCounter := state.generationAttempts + state.stableAddr.localGenerationFailures
|
||||
if oIID := ndp.nic.stack.opaqueIIDOpts; oIID.NICNameFromID != nil {
|
||||
addrBytes = header.AppendOpaqueInterfaceIdentifier(
|
||||
addrBytes[:header.IIDOffsetInIPv6Address],
|
||||
prefix,
|
||||
oIID.NICNameFromID(ndp.nic.ID(), ndp.nic.name),
|
||||
state.generationAttempts,
|
||||
dadCounter,
|
||||
oIID.SecretKey,
|
||||
)
|
||||
} else if state.generationAttempts == 0 {
|
||||
} else if dadCounter == 0 {
|
||||
// Modified-EUI64 based IIDs have no way to resolve DAD conflicts, so if
|
||||
// the DAD counter is non-zero, we cannot use this method.
|
||||
//
|
||||
// Only attempt to generate an interface-specific IID if we have a valid
|
||||
// link address.
|
||||
//
|
||||
|
@ -1246,22 +1265,29 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
|
|||
return false
|
||||
}
|
||||
|
||||
// Generate an address within prefix from the modified EUI-64 of ndp's NIC's
|
||||
// Ethernet MAC address.
|
||||
// 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.
|
||||
// We have no way to regenerate an address in response to an address
|
||||
// conflict when addresses are not generated with opaque IIDs.
|
||||
return false
|
||||
}
|
||||
|
||||
generatedAddr := tcpip.AddressWithPrefix{
|
||||
generatedAddr = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(addrBytes),
|
||||
PrefixLen: validPrefixLenForAutoGen,
|
||||
}
|
||||
|
||||
if !ndp.nic.hasPermanentAddrLocked(generatedAddr.Address) {
|
||||
break
|
||||
}
|
||||
|
||||
state.stableAddr.localGenerationFailures++
|
||||
}
|
||||
|
||||
if ref := ndp.addSLAACAddr(generatedAddr, slaac, time.Since(state.preferredUntil) >= 0 /* deprecated */); ref != nil {
|
||||
state.stableAddrRef = ref
|
||||
state.stableAddr.ref = ref
|
||||
state.generationAttempts++
|
||||
return true
|
||||
}
|
||||
|
@ -1315,7 +1341,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
|||
return false
|
||||
}
|
||||
|
||||
stableAddr := prefixState.stableAddrRef.ep.ID().LocalAddress
|
||||
stableAddr := prefixState.stableAddr.ref.ep.ID().LocalAddress
|
||||
now := time.Now()
|
||||
|
||||
// As per RFC 4941 section 3.3 step 4, the valid lifetime of a temporary
|
||||
|
@ -1354,7 +1380,20 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
|||
return false
|
||||
}
|
||||
|
||||
generatedAddr := header.GenerateTempIPv6SLAACAddr(ndp.temporaryIIDHistory[:], stableAddr)
|
||||
// Attempt to generate a new address that is not already assigned to the NIC.
|
||||
var generatedAddr tcpip.AddressWithPrefix
|
||||
for i := 0; ; i++ {
|
||||
// If we were unable to generate an address after the maximum SLAAC address
|
||||
// local regeneration attempts, do nothing further.
|
||||
if i == maxSLAACAddrLocalRegenAttempts {
|
||||
return false
|
||||
}
|
||||
|
||||
generatedAddr = header.GenerateTempIPv6SLAACAddr(ndp.temporaryIIDHistory[:], stableAddr)
|
||||
if !ndp.nic.hasPermanentAddrLocked(generatedAddr.Address) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// As per RFC RFC 4941 section 3.3 step 5, we MUST NOT create a temporary
|
||||
// address with a zero preferred lifetime. The checks above ensure this
|
||||
|
@ -1450,9 +1489,9 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
|||
// If the preferred lifetime is zero, then the prefix should be deprecated.
|
||||
deprecated := pl == 0
|
||||
if deprecated {
|
||||
ndp.deprecateSLAACAddress(prefixState.stableAddrRef)
|
||||
ndp.deprecateSLAACAddress(prefixState.stableAddr.ref)
|
||||
} else {
|
||||
prefixState.stableAddrRef.deprecated = false
|
||||
prefixState.stableAddr.ref.deprecated = false
|
||||
}
|
||||
|
||||
// If prefix was preferred for some finite lifetime before, stop the
|
||||
|
@ -1514,7 +1553,7 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
|||
|
||||
// If DAD is not yet complete on the stable address, there is no need to do
|
||||
// work with temporary addresses.
|
||||
if prefixState.stableAddrRef.getKind() != permanent {
|
||||
if prefixState.stableAddr.ref.getKind() != permanent {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1617,7 +1656,7 @@ func (ndp *ndpState) deprecateSLAACAddress(ref *referencedNetworkEndpoint) {
|
|||
//
|
||||
// The NIC that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, state slaacPrefixState) {
|
||||
if r := state.stableAddrRef; r != nil {
|
||||
if r := state.stableAddr.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 /* allowSLAACInvalidation */); err != nil {
|
||||
|
@ -1639,14 +1678,14 @@ func (ndp *ndpState) cleanupSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPr
|
|||
|
||||
prefix := addr.Subnet()
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok || state.stableAddrRef == nil || addr.Address != state.stableAddrRef.ep.ID().LocalAddress {
|
||||
if !ok || state.stableAddr.ref == nil || addr.Address != state.stableAddr.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.stableAddrRef = nil
|
||||
state.stableAddr.ref = nil
|
||||
ndp.slaacPrefixes[prefix] = state
|
||||
return
|
||||
}
|
||||
|
@ -1665,7 +1704,7 @@ func (ndp *ndpState) cleanupSLAACPrefixResources(prefix tcpip.Subnet, state slaa
|
|||
ndp.invalidateTempSLAACAddr(state.tempAddrs, tempAddr, tempAddrState)
|
||||
}
|
||||
|
||||
state.stableAddrRef = nil
|
||||
state.stableAddr.ref = nil
|
||||
state.deprecationTimer.StopLocked()
|
||||
state.invalidationTimer.StopLocked()
|
||||
delete(ndp.slaacPrefixes, prefix)
|
||||
|
|
|
@ -2521,6 +2521,215 @@ func TestAutoGenTempAddrRegenTimerUpdates(t *testing.T) {
|
|||
expectAutoGenAddrEventAsync(tempAddr3, newAddr, regenAfter+defaultAsyncEventTimeout)
|
||||
}
|
||||
|
||||
// TestMixedSLAACAddrConflictRegen tests SLAAC address regeneration in response
|
||||
// to a mix of DAD conflicts and NIC-local conflicts.
|
||||
func TestMixedSLAACAddrConflictRegen(t *testing.T) {
|
||||
const (
|
||||
nicID = 1
|
||||
nicName = "nic"
|
||||
lifetimeSeconds = 9999
|
||||
// From stack.maxSLAACAddrLocalRegenAttempts
|
||||
maxSLAACAddrLocalRegenAttempts = 10
|
||||
// We use 2 more addreses than the maximum local regeneration attempts
|
||||
// because we want to also trigger regeneration in response to a DAD
|
||||
// conflicts for this test.
|
||||
maxAddrs = maxSLAACAddrLocalRegenAttempts + 2
|
||||
dupAddrTransmits = 1
|
||||
retransmitTimer = time.Second
|
||||
)
|
||||
|
||||
var tempIIDHistoryWithModifiedEUI64 [header.IIDSize]byte
|
||||
header.InitialTempIID(tempIIDHistoryWithModifiedEUI64[:], nil, nicID)
|
||||
|
||||
var tempIIDHistoryWithOpaqueIID [header.IIDSize]byte
|
||||
header.InitialTempIID(tempIIDHistoryWithOpaqueIID[:], nil, nicID)
|
||||
|
||||
prefix, subnet, stableAddrWithModifiedEUI64 := prefixSubnetAddr(0, linkAddr1)
|
||||
var stableAddrsWithOpaqueIID [maxAddrs]tcpip.AddressWithPrefix
|
||||
var tempAddrsWithOpaqueIID [maxAddrs]tcpip.AddressWithPrefix
|
||||
var tempAddrsWithModifiedEUI64 [maxAddrs]tcpip.AddressWithPrefix
|
||||
addrBytes := []byte(subnet.ID())
|
||||
for i := 0; i < maxAddrs; i++ {
|
||||
stableAddrsWithOpaqueIID[i] = tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet, nicName, uint8(i), nil)),
|
||||
PrefixLen: header.IIDOffsetInIPv6Address * 8,
|
||||
}
|
||||
// When generating temporary addresses, the resolved stable address for the
|
||||
// SLAAC prefix will be the first address stable address generated for the
|
||||
// prefix as we will not simulate address conflicts for the stable addresses
|
||||
// in tests involving temporary addresses. Address conflicts for stable
|
||||
// addresses will be done in their own tests.
|
||||
tempAddrsWithOpaqueIID[i] = header.GenerateTempIPv6SLAACAddr(tempIIDHistoryWithOpaqueIID[:], stableAddrsWithOpaqueIID[0].Address)
|
||||
tempAddrsWithModifiedEUI64[i] = header.GenerateTempIPv6SLAACAddr(tempIIDHistoryWithModifiedEUI64[:], stableAddrWithModifiedEUI64.Address)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
addrs []tcpip.AddressWithPrefix
|
||||
tempAddrs bool
|
||||
initialExpect tcpip.AddressWithPrefix
|
||||
nicNameFromID func(tcpip.NICID, string) string
|
||||
}{
|
||||
{
|
||||
name: "Stable addresses with opaque IIDs",
|
||||
addrs: stableAddrsWithOpaqueIID[:],
|
||||
nicNameFromID: func(tcpip.NICID, string) string {
|
||||
return nicName
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Temporary addresses with opaque IIDs",
|
||||
addrs: tempAddrsWithOpaqueIID[:],
|
||||
tempAddrs: true,
|
||||
initialExpect: stableAddrsWithOpaqueIID[0],
|
||||
nicNameFromID: func(tcpip.NICID, string) string {
|
||||
return nicName
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Temporary addresses with modified EUI64",
|
||||
addrs: tempAddrsWithModifiedEUI64[:],
|
||||
tempAddrs: true,
|
||||
initialExpect: stableAddrWithModifiedEUI64,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ndpDisp := ndpDispatcher{
|
||||
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2),
|
||||
}
|
||||
e := channel.New(0, 1280, linkAddr1)
|
||||
ndpConfigs := stack.NDPConfigurations{
|
||||
HandleRAs: true,
|
||||
AutoGenGlobalAddresses: true,
|
||||
AutoGenTempGlobalAddresses: test.tempAddrs,
|
||||
AutoGenAddressConflictRetries: 1,
|
||||
}
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv6.NewProtocol()},
|
||||
TransportProtocols: []stack.TransportProtocol{udp.NewProtocol()},
|
||||
NDPConfigs: ndpConfigs,
|
||||
NDPDisp: &ndpDisp,
|
||||
OpaqueIIDOpts: stack.OpaqueInterfaceIdentifierOptions{
|
||||
NICNameFromID: test.nicNameFromID,
|
||||
},
|
||||
})
|
||||
|
||||
s.SetRouteTable([]tcpip.Route{{
|
||||
Destination: header.IPv6EmptySubnet,
|
||||
Gateway: llAddr2,
|
||||
NIC: nicID,
|
||||
}})
|
||||
|
||||
if err := s.CreateNIC(nicID, e); err != nil {
|
||||
t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
|
||||
}
|
||||
|
||||
for j := 0; j < len(test.addrs)-1; j++ {
|
||||
// The NIC will not attempt to generate an address in response to a
|
||||
// NIC-local conflict after some maximum number of attempts. We skip
|
||||
// creating a conflict for the address that would be generated as part
|
||||
// of the last attempt so we can simulate a DAD conflict for this
|
||||
// address and restart the NIC-local generation process.
|
||||
if j == maxSLAACAddrLocalRegenAttempts-1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := s.AddAddress(nicID, ipv6.ProtocolNumber, test.addrs[j].Address); err != nil {
|
||||
t.Fatalf("s.AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, test.addrs[j].Address, 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")
|
||||
}
|
||||
}
|
||||
|
||||
expectAutoGenAddrAsyncEvent := 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)
|
||||
}
|
||||
case <-time.After(defaultAsyncEventTimeout):
|
||||
t.Fatal("timed out waiting for addr auto gen event")
|
||||
}
|
||||
}
|
||||
|
||||
expectDADEventAsync := func(addr tcpip.Address) {
|
||||
t.Helper()
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.dadC:
|
||||
if diff := checkDADEvent(e, nicID, addr, true, nil); diff != "" {
|
||||
t.Errorf("dad event mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
case <-time.After(dupAddrTransmits*retransmitTimer + defaultAsyncEventTimeout):
|
||||
t.Fatal("timed out waiting for DAD event")
|
||||
}
|
||||
}
|
||||
|
||||
// Enable DAD.
|
||||
ndpDisp.dadC = make(chan ndpDADEvent, 2)
|
||||
ndpConfigs.DupAddrDetectTransmits = dupAddrTransmits
|
||||
ndpConfigs.RetransmitTimer = retransmitTimer
|
||||
if err := s.SetNDPConfigurations(nicID, ndpConfigs); err != nil {
|
||||
t.Fatalf("s.SetNDPConfigurations(%d, _): %s", nicID, err)
|
||||
}
|
||||
|
||||
// Do SLAAC for prefix.
|
||||
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, lifetimeSeconds, lifetimeSeconds))
|
||||
if test.initialExpect != (tcpip.AddressWithPrefix{}) {
|
||||
expectAutoGenAddrEvent(test.initialExpect, newAddr)
|
||||
expectDADEventAsync(test.initialExpect.Address)
|
||||
}
|
||||
|
||||
// The last local generation attempt should succeed, but we introduce a
|
||||
// DAD failure to restart the local generation process.
|
||||
addr := test.addrs[maxSLAACAddrLocalRegenAttempts-1]
|
||||
expectAutoGenAddrAsyncEvent(addr, newAddr)
|
||||
if err := s.DupTentativeAddrDetected(nicID, addr.Address); err != nil {
|
||||
t.Fatalf("s.DupTentativeAddrDetected(%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")
|
||||
}
|
||||
expectAutoGenAddrEvent(addr, invalidatedAddr)
|
||||
|
||||
// The last address generated should resolve DAD.
|
||||
addr = test.addrs[len(test.addrs)-1]
|
||||
expectAutoGenAddrAsyncEvent(addr, newAddr)
|
||||
expectDADEventAsync(addr.Address)
|
||||
|
||||
select {
|
||||
case e := <-ndpDisp.autoGenAddrC:
|
||||
t.Fatalf("unexpected auto gen addr event = %+v", e)
|
||||
default:
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// stackAndNdpDispatcherWithDefaultRoute returns an ndpDispatcher,
|
||||
// channel.Endpoint and stack.Stack.
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue