diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index a58646c00..84331f042 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -1965,7 +1965,10 @@ exit: ThreadError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Child &aChild) { const AddressRegistrationEntry *entry; + Ip6::Address address; Lowpan::Context context; + Child *child; + uint8_t index; aChild.ClearIp6Addresses(); @@ -1980,12 +1983,39 @@ ThreadError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, { // xxx check if context id exists mNetif.GetNetworkDataLeader().GetContext(entry->GetContextId(), context); - memcpy(&aChild.GetIp6Address(count), context.mPrefix, BitVectorBytes(context.mPrefixLength)); - aChild.GetIp6Address(count).SetIid(entry->GetIid()); + memcpy(&address, context.mPrefix, BitVectorBytes(context.mPrefixLength)); + address.SetIid(entry->GetIid()); } else { - aChild.GetIp6Address(count) = *entry->GetIp6Address(); + address = *entry->GetIp6Address(); + } + + aChild.GetIp6Address(count) = address; + + // We check if the same address is in-use by another child, if so + // remove it. This implements "last-in wins" duplicate address + // resolution policy. + // + // Duplicate addresses can occur if a previously attached child + // attaches to same parent again (after a reset, memory wipe) using + // a new random extended address before the old entry in the child + // table is timed out and then trying to register its globally unique + // IPv6 address as the new child. + + for (int i = 0; i < mMaxChildrenAllowed; i++) + { + child = &mChildren[i]; + + if (!child->IsStateValidOrRestoring() || (child == &aChild)) + { + continue; + } + + if (child->FindIp6Address(address, &index) == kThreadError_None) + { + child->RemoveIp6Address(index); + } } } @@ -3253,12 +3283,9 @@ Neighbor *MleRouter::GetNeighbor(const Ip6::Address &aAddress) ExitNow(rval = child); } - for (uint8_t j = 0; j < Child::kMaxIp6AddressPerChild; j++) + if (child->FindIp6Address(aAddress, NULL) == kThreadError_None) { - if (child->GetIp6Address(j) == aAddress) - { - ExitNow(rval = child); - } + ExitNow(rval = child); } } diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index 225e85be3..4c6d45f2c 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -54,6 +54,42 @@ void Neighbor::GenerateChallenge(void) } } +ThreadError Child::FindIp6Address(const Ip6::Address &aAddress, uint8_t *aIndex) const +{ + ThreadError error = kThreadError_NotFound; + + for (uint8_t index = 0; index < kMaxIp6AddressPerChild; index++) + { + if (mIp6Address[index] == aAddress) + { + if (aIndex != NULL) + { + *aIndex = index; + } + + error = kThreadError_None; + break; + } + } + + return error; +} + +void Child::RemoveIp6Address(uint8_t aIndex) +{ + VerifyOrExit(aIndex < kMaxIp6AddressPerChild); + + for (uint8_t i = aIndex; i < kMaxIp6AddressPerChild - 1; i++) + { + mIp6Address[i] = mIp6Address[i + 1]; + } + + memset(&mIp6Address[kMaxIp6AddressPerChild - 1], 0, sizeof(Ip6::Address)); + +exit: + return; +} + void Child::GenerateChallenge(void) { for (uint8_t i = 0; i < sizeof(mAttachChallenge); i++) diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index fab95b15a..4cb9b8890 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -86,7 +86,7 @@ public: * Check if the neighbor/child is in valid state or if it is being restored. * When in these states messages can be sent to and/or received from the neighbor/child. * - * @returns `true` if the neighbor is in valid, restored, or being restored states, `false` otherwise. + * @returns TRUE if the neighbor is in valid, restored, or being restored states, FALSE otherwise. * */ bool IsStateValidOrRestoring(void) const { return (mState == kStateValid) || (mState == kStateRestored); } @@ -370,6 +370,28 @@ public: */ Ip6::Address &GetIp6Address(uint8_t aIndex) { return mIp6Address[aIndex]; } + /** + * This method searches for a given IPv6 address in the child's IPv6 address list and provides the index of the + * address in the list if it is found. + * + * @param[in] aAddress The IPv6 address to search for in the IPv6 address list. + * @param[out] aIndex Pointer to variable where the index of address is provided if address is found in + * the list. @p aIndex can be set NULL if index is not required. + * + * @retval kThreadError_None Successfully found the address in IPv6 address list and updated @p aIndex. + * @retval kThreadError_NotFound Could not find the address in the list. + * + */ + ThreadError FindIp6Address(const Ip6::Address &aAddress, uint8_t *aIndex) const; + + /** + * This method removes the address at index @p aIndex. + * + * @param[in] aIndex The index into the IPv6 address list. + * + */ + void RemoveIp6Address(uint8_t aIndex); + /** * This method gets the child timeout. *