diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 738a26580..3b49e83df 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -273,22 +273,28 @@ otError otThreadGetChildInfoByIndex(otInstance *aInstance, uint16_t aChildIndex, otError otThreadGetChildNextIp6Address(otInstance * aInstance, uint16_t aChildIndex, - otChildIp6AddressIterator *aIterator, + otChildIp6AddressIterator *aIterIndex, otIp6Address * aAddress) { - otError error = OT_ERROR_NONE; - Instance & instance = *static_cast(aInstance); - Child::Ip6AddressIterator iterator; - Ip6::Address * address; + otError error = OT_ERROR_NONE; + Instance & instance = *static_cast(aInstance); + const Child *child; - OT_ASSERT(aIterator != nullptr && aAddress != nullptr); + OT_ASSERT(aIterIndex != nullptr && aAddress != nullptr); - address = static_cast(aAddress); - iterator.Set(*aIterator); + child = instance.Get().GetChildAtIndex(aChildIndex); + VerifyOrExit(child != nullptr, error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(child->IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS); - SuccessOrExit(error = instance.Get().GetChildNextIp6Address(aChildIndex, iterator, *address)); + { + Child::AddressIterator iter(*child, *aIterIndex); - *aIterator = iterator.Get(); + VerifyOrExit(!iter.IsDone(), error = OT_ERROR_NOT_FOUND); + *aAddress = *iter.GetAddress(); + + iter++; + *aIterIndex = iter.GetAsIndex(); + } exit: return error; diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 413d52824..5b053d03c 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -369,6 +369,31 @@ uint8_t Address::PrefixMatch(const otIp6Address &aOther) const return PrefixMatch(mFields.m8, aOther.mFields.m8, sizeof(Address)); } +bool Address::MatchesFilter(TypeFilter aFilter) const +{ + bool matches = true; + + switch (aFilter) + { + case kTypeAny: + break; + + case kTypeUnicast: + matches = !IsUnspecified() && !IsMulticast(); + break; + + case kTypeMulticast: + matches = IsMulticast(); + break; + + case kTypeMulticastLargerThanRealmLocal: + matches = IsMulticastLargerThanRealmLocal(); + break; + } + + return matches; +} + otError Address::FromString(const char *aBuf) { otError error = OT_ERROR_NONE; diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index 7c230196e..4fb4d9ee3 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -324,6 +324,18 @@ public: kGlobalScope = 14, ///< Global scope }; + /** + * This enumeration defines IPv6 address type filter. + * + */ + enum TypeFilter : uint8_t + { + kTypeAny, ///< Accept any IPv6 address (unicast or multicast). + kTypeUnicast, ///< Accept unicast IPv6 addresses only. + kTypeMulticast, ///< Accept multicast IPv6 addresses only. + kTypeMulticastLargerThanRealmLocal, ///< Accept multicast IPv6 addresses with scope larger than Realm Local. + }; + /** * This type defines the fixed-length `String` object returned from `ToString()`. * @@ -618,6 +630,17 @@ public: */ uint8_t PrefixMatch(const otIp6Address &aOther) const; + /** + * This method indicates whether address matches a given type filter. + * + * @param[in] aFilter An address type filter. + * + * @retval TRUE The address matches @p aFilter. + * @retval FALSE The address does not match @p aFilter. + * + */ + bool MatchesFilter(TypeFilter aFilter) const; + /** * This method converts an IPv6 address string to binary. * diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 948d246f5..b58dfc294 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -3640,23 +3640,6 @@ exit: return error; } -otError MleRouter::GetChildNextIp6Address(uint16_t aChildIndex, - Child::Ip6AddressIterator &aIterator, - Ip6::Address & aAddress) -{ - otError error = OT_ERROR_NONE; - Child * child = nullptr; - - child = mChildTable.GetChildAtIndex(aChildIndex); - VerifyOrExit(child != nullptr, error = OT_ERROR_INVALID_ARGS); - VerifyOrExit(child->IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS); - - error = child->GetNextIp6Address(aIterator, aAddress); - -exit: - return error; -} - void MleRouter::RestoreChildren(void) { otError error = OT_ERROR_NONE; @@ -4425,19 +4408,17 @@ otError MleRouter::AppendConnectivity(Message &aMessage) otError MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild) { - otError error; - Ip6::Address address; - Child::Ip6AddressIterator iterator; - Tlv tlv; - AddressRegistrationEntry entry; - Lowpan::Context context; - uint8_t length = 0; - uint16_t startOffset = aMessage.GetLength(); + otError error; + Tlv tlv; + AddressRegistrationEntry entry; + Lowpan::Context context; + uint8_t length = 0; + uint16_t startOffset = aMessage.GetLength(); tlv.SetType(Tlv::kAddressRegistration); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); - while (aChild.GetNextIp6Address(iterator, address) == OT_ERROR_NONE) + for (const Ip6::Address &address : aChild.IterateIp6Addresses()) { if (address.IsMulticast() || Get().GetContext(address, context) != OT_ERROR_NONE) { diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 12379f386..de2f3f3a1 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -441,21 +441,6 @@ public: */ otError GetChildInfoByIndex(uint16_t aChildIndex, otChildInfo &aChildInfo); - /** - * This methods gets the next IPv6 address (using an iterator) for a given child. - * - * @param[in] aChildIndex The child index. - * @param[inout] aIterator A reference to iterator. On success the iterator will be updated to point to next - * entry in the list. - * @param[out] aAddress A reference to an IPv6 address where the child's next address is placed (on success). - * - * @retval OT_ERROR_NONE Successfully found the next address (@p aAddress and @ aIterator are updated). - * @retval OT_ERROR_NOT_FOUND The child has no subsequent IPv6 address entry. - * @retval OT_ERROR_INVALID_ARGS Child at @p aChildIndex is not valid. - * - */ - otError GetChildNextIp6Address(uint16_t aChildIndex, Child::Ip6AddressIterator &aIterator, Ip6::Address &aAddress); - /** * This method indicates whether or not the RLOC16 is an MTD child of this device. * diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index ea910acf1..4daeb6c09 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -111,6 +111,39 @@ void Neighbor::GenerateChallenge(void) Random::Crypto::FillBuffer(mValidPending.mPending.mChallenge, sizeof(mValidPending.mPending.mChallenge))); } +const Ip6::Address *Child::AddressIterator::GetAddress(void) const +{ + // `mIndex` value of zero indicates mesh-local IPv6 address. + // Non-zero value specifies the index into address array starting + // from one for first element (i.e, `mIndex - 1` gives the array + // index). + + return (mIndex == 0) ? &mMeshLocalAddress : ((mIndex < kMaxIndex) ? &mChild.mIp6Address[mIndex - 1] : nullptr); +} + +void Child::AddressIterator::Update(void) +{ + const Ip6::Address *address; + + while (true) + { + if ((mIndex == 0) && (mChild.GetMeshLocalIp6Address(mMeshLocalAddress) != OT_ERROR_NONE)) + { + mIndex++; + } + + address = GetAddress(); + + VerifyOrExit((address != nullptr) && !address->IsUnspecified(), mIndex = kMaxIndex); + + VerifyOrExit(!address->MatchesFilter(mFilter), OT_NOOP); + mIndex++; + } + +exit: + return; +} + void Child::Clear(void) { Instance &instance = GetInstance(); @@ -138,31 +171,6 @@ exit: return error; } -otError Child::GetNextIp6Address(Ip6AddressIterator &aIterator, Ip6::Address &aAddress) const -{ - otError error = OT_ERROR_NONE; - otChildIp6AddressIterator index; - - // Index zero corresponds to the Mesh Local IPv6 address (if any). - - if (aIterator.Get() == 0) - { - aIterator.Increment(); - VerifyOrExit(GetMeshLocalIp6Address(aAddress) == OT_ERROR_NOT_FOUND, OT_NOOP); - } - - index = aIterator.Get() - 1; - - VerifyOrExit(index < kNumIp6Addresses, error = OT_ERROR_NOT_FOUND); - - VerifyOrExit(!mIp6Address[index].IsUnspecified(), error = OT_ERROR_NOT_FOUND); - aAddress = mIp6Address[index]; - aIterator.Increment(); - -exit: - return error; -} - otError Child::AddIp6Address(const Ip6::Address &aAddress) { otError error = OT_ERROR_NONE; diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 7599ffd0c..d1c95fabf 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -497,6 +497,8 @@ private: */ class Child : public Neighbor, public IndirectSender::ChildInfo, public DataPollHandler::ChildInfo { + class AddressIteratorBuilder; + public: enum { @@ -504,53 +506,160 @@ public: }; /** - * This class defines an iterator used by `GetNextIp6Address()` to go through IPv6 address entries of a child. + * This class defines an iterator used to go through IPv6 address entries of a child. * */ - class Ip6AddressIterator + class AddressIterator { - friend class Child; + friend class AddressIteratorBuilder; public: /** - * This constructor initializes the iterator object. - * - * After initialization a call to `GetNextIp6Address()` would start at the first IPv6 address entry in the list. + * This type represents an index indicating the current IPv6 address entry to which the iterator is pointing. * */ - Ip6AddressIterator(void) - : mIndex(0) + typedef otChildIp6AddressIterator Index; + + /** + * This constructor initializes the iterator associated with a given `Child` starting from beginning of the + * IPv6 address list. + * + * @param[in] aChild A reference to a child entry. + * @param[in] aFilter An IPv6 address type filter restricting iterator to certain type of addresses. + * + */ + AddressIterator(const Child &aChild, Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) + : AddressIterator(aChild, 0, aFilter) { } /** - * This method resets the iterator. + * This constructor initializes the iterator associated with a given `Child` starting from a given index * - * After reset the next call to `GetNextIp6Address()` would start at the first IPv6 address entry in the list. + * @param[in] aChild A reference to the child entry. + * @param[in] aIndex An index (`Index`) with which to initialize the iterator. + * @param[in] aFilter An IPv6 address type filter restricting iterator to certain type of addresses. * */ - void Reset(void) { mIndex = 0; } + AddressIterator(const Child &aChild, Index aIndex, Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) + : mChild(aChild) + , mFilter(aFilter) + , mIndex(aIndex) + { + Update(); + } /** - * This method sets the iterator from an `otChildIp6AddressIterator` + * This method converts the iterator into an index. * - * @param[in] aChildAddressIterator A child address iterator + * @returns An index corresponding to the iterator. * */ - void Set(otChildIp6AddressIterator aChildAddressIterator) { mIndex = aChildAddressIterator; } + Index GetAsIndex(void) const { return mIndex; } /** - * This method returns the iterator as an `otChildIp6AddressIterator` + * This method gets the iterator's associated `Child` entry. * - * @returns The iterator as an `otChildIp6AddressIterator` + * @returns The associated child entry. * */ - otChildIp6AddressIterator Get(void) const { return mIndex; } + const Child &GetChild(void) const { return mChild; } + + /** + * This method gets the current `Child` IPv6 Address to which the iterator is pointing. + * + * @returns A pointer to the associated IPv6 Address, or nullptr if iterator is done. + * + */ + const Ip6::Address *GetAddress(void) const; + + /** + * This method indicates whether the iterator has reached end of the list. + * + * @retval TRUE There are no more entries in the list (reached end of the list). + * @retval FALSE The current entry is valid. + * + */ + bool IsDone(void) const { return (mIndex >= kMaxIndex); } + + /** + * This method overloads `++` operator (pre-increment) to advance the iterator. + * + * The iterator is moved to point to the next `Address` entry. If there are no more `Ip6::Address` entries + * `IsDone()` returns `true`. + * + */ + void operator++(void) { mIndex++, Update(); } + + /** + * This method overloads `++` operator (post-increment) to advance the iterator. + * + * The iterator is moved to point to the next `Address` entry. If there are no more `Ip6::Address` entries + * `IsDone()` returns `true`. + * + */ + void operator++(int) { mIndex++, Update(); } + + /** + * This method overloads the `*` dereference operator and gets a reference to `Ip6::Address` to which the + * iterator is currently pointing. + * + * This method MUST be used when the iterator is not done (i.e., `IsDone()` returns `false`). + * + * @returns A reference to the `Ip6::Address` entry currently pointed by the iterator. + * + */ + const Ip6::Address &operator*(void)const { return *GetAddress(); } + + /** + * This method overloads operator `==` to evaluate whether or not two `Iterator` instances are equal. + * + * This method MUST be used when the two iterators are associated with the same `Child` entry. + * + * @param[in] aOther The other `Iterator` to compare with. + * + * @retval TRUE If the two `Iterator` objects are equal. + * @retval FALSE If the two `Iterator` objects are not equal. + * + */ + bool operator==(const AddressIterator &aOther) const { return (mIndex == aOther.mIndex); } + + /** + * This method overloads operator `!=` to evaluate whether or not two `Iterator` instances are unequal. + * + * This method MUST be used when the two iterators are associated with the same `Child` entry. + * + * @param[in] aOther The other `Iterator` to compare with. + * + * @retval TRUE If the two `Iterator` objects are unequal. + * @retval FALSE If the two `Iterator` objects are not unequal. + * + */ + bool operator!=(const AddressIterator &aOther) const { return !(*this == aOther); } private: - void Increment(void) { mIndex++; } + enum IteratorType : uint8_t + { + kEndIterator, + }; - otChildIp6AddressIterator mIndex; + enum : uint16_t + { + kMaxIndex = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD, + }; + + AddressIterator(const Child &aChild, IteratorType) + : mChild(aChild) + , mIndex(kMaxIndex) + { + } + + void Update(void); + + const Child & mChild; + Ip6::Address::TypeFilter mFilter; + Index mIndex; + Ip6::Address mMeshLocalAddress; }; /** @@ -593,16 +702,26 @@ public: const Ip6::InterfaceIdentifier &GetMeshLocalIid(void) const { return mMeshLocalIid; } /** - * This method gets the next IPv6 address in the list. + * This method enables range-based `for` loop iteration over all (or a subset of) IPv6 addresses. * - * @param[inout] aIterator A reference to an IPv6 address iterator. - * @param[out] aAddress A reference to an IPv6 address to provide the next address (if any). + * This method should be used as follows: to iterate over all addresses * - * @retval OT_ERROR_NONE Successfully found the next address and updated @p aAddress and @p aIterator. - * @retval OT_ERROR_NOT_FOUND No subsequent IPv6 address exists in the IPv6 address list. + * for (const Ip6::Address &address : child.IterateIp6Addresses()) { ... } + * + * or to iterate over a subset of IPv6 addresses determined by a given address type filter + * + * for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticast)) { ... } + * + * @param[in] aFilter An IPv6 address type filter restricting iteration to certain type of addresses (default is + * to accept any address type). + * + * @returns An IteratorBuilder instance. * */ - otError GetNextIp6Address(Ip6AddressIterator &aIterator, Ip6::Address &aAddress) const; + AddressIteratorBuilder IterateIp6Addresses(Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) const + { + return AddressIteratorBuilder(*this, aFilter); + } /** * This method adds an IPv6 address to the list. @@ -753,6 +872,23 @@ private: kNumIp6Addresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1, }; + class AddressIteratorBuilder + { + public: + AddressIteratorBuilder(const Child &aChild, Ip6::Address::TypeFilter aFilter) + : mChild(aChild) + , mFilter(aFilter) + { + } + + AddressIterator begin(void) { return AddressIterator(mChild, mFilter); } + AddressIterator end(void) { return AddressIterator(mChild, AddressIterator::kEndIterator); } + + private: + const Child & mChild; + Ip6::Address::TypeFilter mFilter; + }; + uint8_t mNetworkDataVersion; ///< Current Network Data version Ip6::InterfaceIdentifier mMeshLocalIid; ///< IPv6 address IID for mesh-local address Ip6::Address mIp6Address[kNumIp6Addresses]; ///< Registered IPv6 addresses diff --git a/tests/unit/test_child.cpp b/tests/unit/test_child.cpp index fcb471e0e..7f23363c7 100644 --- a/tests/unit/test_child.cpp +++ b/tests/unit/test_child.cpp @@ -46,11 +46,9 @@ enum void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, const Ip6::Address aAddressList[]) { - Child::Ip6AddressIterator iterator; - Ip6::Address address; - bool addressObserved[kMaxChildIp6Addresses]; - bool addressIsMeshLocal[kMaxChildIp6Addresses]; - bool hasMeshLocal = false; + Ip6::Address::TypeFilter filters[] = {Ip6::Address::kTypeUnicast, Ip6::Address::kTypeMulticast}; + bool addressObserved[kMaxChildIp6Addresses]; + bool hasMeshLocal = false; for (uint8_t index = 0; index < aAddressListLength; index++) { @@ -58,16 +56,8 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co } memset(addressObserved, 0, sizeof(addressObserved)); - memset(addressIsMeshLocal, 0, sizeof(addressObserved)); - for (uint8_t index = 0; index < aAddressListLength; index++) - { - { - addressIsMeshLocal[index] = true; - } - } - - while (aChild.GetNextIp6Address(iterator, address) == OT_ERROR_NONE) + for (const Ip6::Address &address : aChild.IterateIp6Addresses()) { bool addressIsInList = false; @@ -81,12 +71,14 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co } } - VerifyOrQuit(addressIsInList, "Child::GetNextIp6Address() returned an address not in the expected list"); + VerifyOrQuit(addressIsInList, "Child::IterateIp6Addresses() returned an address not in the expected list"); } for (uint8_t index = 0; index < aAddressListLength; index++) { - VerifyOrQuit(addressObserved[index], "Child::GetNextIp6Address() missed an entry from the expected list"); + Ip6::Address address; + + VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list"); if (sInstance->Get().IsMeshLocalAddress(aAddressList[index])) { @@ -98,9 +90,100 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co if (!hasMeshLocal) { + Ip6::Address address; + VerifyOrQuit(aChild.GetMeshLocalIp6Address(address) == OT_ERROR_NOT_FOUND, "Child::GetMeshLocalIp6Address() returned an address not in the expected list"); } + + // Iterate over unicast and multicast addresses separately. + + memset(addressObserved, 0, sizeof(addressObserved)); + + for (Ip6::Address::TypeFilter filter : filters) + { + for (const Ip6::Address &address : aChild.IterateIp6Addresses(filter)) + { + bool addressIsInList = false; + + switch (filter) + { + case Ip6::Address::kTypeMulticast: + VerifyOrQuit(address.IsMulticast(), "Address::TypeFilter failed"); + break; + + case Ip6::Address::kTypeUnicast: + VerifyOrQuit(!address.IsMulticast(), "Address::TypeFilter failed"); + break; + + default: + break; + } + + VerifyOrQuit(address.MatchesFilter(filter), "Address::MatchesFilter() failed"); + + for (uint8_t index = 0; index < aAddressListLength; index++) + { + if (address == aAddressList[index]) + { + VerifyOrQuit(addressObserved[index] == false, + "Child::IterateIp6Addresses() returned duplicate addr"); + addressObserved[index] = true; + addressIsInList = true; + break; + } + } + + VerifyOrQuit(addressIsInList, "Child::IterateIp6Addresses() returned an address not in the expected list"); + } + } + + for (uint8_t index = 0; index < aAddressListLength; index++) + { + VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list"); + } + + // Verify behavior of `Child::AddressIterator + { + Child::AddressIterator iter1(aChild); + Child::AddressIterator iter2(aChild); + Child::AddressIterator::Index iterIndex; + + for (const Ip6::Address &address : aChild.IterateIp6Addresses()) + { + VerifyOrQuit(iter1 == iter2, "AddressIterator:operator== failed"); + VerifyOrQuit(!iter1.IsDone(), "AddressIterator::IsDone() failed"); + VerifyOrQuit(*iter1.GetAddress() == address, "AddressIterator::GetAddress() failed"); + VerifyOrQuit(*iter1.GetAddress() == *iter2.GetAddress(), "AddressIterator::GetAddress() failed"); + + iterIndex = iter1.GetAsIndex(); + VerifyOrQuit(iter2.GetAsIndex() == iterIndex, "AddressIterator: GetAsIndex() failed"); + + { + Child::AddressIterator iter3(aChild, iterIndex); + VerifyOrQuit(iter3 == iter1, "AddressIterator(iterIndex) failed"); + + iter3++; + VerifyOrQuit(iter3 != iter1, "AddressIterator(iterIndex) failed"); + } + + iter1++; + VerifyOrQuit(iter1 != iter2, "AddressIterator:operator!= failed"); + iter2++; + } + + VerifyOrQuit(iter1.IsDone(), "AddressIterator::IsDone() failed"); + VerifyOrQuit(iter2.IsDone(), "AddressIterator::IsDone() failed"); + VerifyOrQuit(iter1 == iter2, "AddressIterator:operator== failed"); + + iterIndex = iter1.GetAsIndex(); + VerifyOrQuit(iter2.GetAsIndex() == iterIndex, "AddressIterator: GetAsIndex() failed"); + + { + Child::AddressIterator iter3(aChild, iterIndex); + VerifyOrQuit(iter3 == iter1, "AddressIterator(iterIndex) failed"); + } + } } void TestChildIp6Address(void) @@ -110,7 +193,7 @@ void TestChildIp6Address(void) uint8_t numAddresses; const char * ip6Addresses[] = { "fd00:1234::1234", - "fd6b:e251:52fb:0:12e6:b94c:1c28:c56a", + "ff6b:e251:52fb:0:12e6:b94c:1c28:c56a", "fd00:1234::204c:3d7c:98f6:9a1b", };