From 5e6e262dfb41d6869818bb76fe13265a4e391811 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 22 Jan 2018 08:52:14 -0800 Subject: [PATCH] [child] update how IPv6 addresses of a child are managed (#2485) This commit updates/enhances the `Child` class methods related to managing of the list of IPv6 addresses associated with a child. The new model provides APIs to add or remove an IPv6 address, check if the list contains an address, and a method to iterate through all IPv6 addresses of a child. The `mle_router` and `address_resolver` and NCP implementations are updated to use the new APIs. This commit also adds a unit test `test_child` to verify the newly added methods. --- include/openthread/thread_ftd.h | 20 +++ include/openthread/types.h | 6 +- src/core/api/thread_ftd_api.cpp | 22 +++ src/core/net/ip6_address.cpp | 5 + src/core/net/ip6_address.hpp | 6 + src/core/thread/address_resolver.cpp | 36 ++-- src/core/thread/mle_router.cpp | 68 +++++--- src/core/thread/mle_router_ftd.hpp | 15 ++ src/core/thread/topology.cpp | 84 ++++++++-- src/core/thread/topology.hpp | 107 +++++++++--- src/ncp/ncp_base_ftd.cpp | 12 +- tests/unit/Makefile.am | 4 + tests/unit/test_child.cpp | 237 +++++++++++++++++++++++++++ 13 files changed, 530 insertions(+), 92 deletions(-) create mode 100644 tests/unit/test_child.cpp diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index c7d947f8a..5d36cb1f7 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -365,6 +365,26 @@ OTAPI otError OTCALL otThreadGetChildInfoById(otInstance *aInstance, uint16_t aC */ OTAPI otError OTCALL otThreadGetChildInfoByIndex(otInstance *aInstance, uint8_t aChildIndex, otChildInfo *aChildInfo); +/** + * This function gets the next IPv6 address (using an iterator) for a given child. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aChildIndex The child index. + * @param[inout] aIterator A pointer to the iterator. On success the iterator will be updated to point to next + * entry in the list. To get the first IPv6 address the iterator should be set to + * OT_CHILD_IP6_ADDRESS_ITERATOR_INIT. + * @param[out] aAddress A pointer to an IPv6 address where the child's next address is placed (on success). + * + * @retval OT_ERROR_NONE Successfully found the next IPv6 address (@p aAddress was successfully updated). + * @retval OT_ERROR_NOT_FOUND The child has no subsequent IPv6 address entry. + * @retval OT_ERROR_INVALID_ARGS @p aIterator or @p aAddress are NULL, or child at @p aChildIndex is not valid. + * + * @sa otThreadGetChildInfoByIndex + * + */ +otError otThreadGetChildNextIp6Address(otInstance *aInstance, uint8_t aChildIndex, otChildIp6AddressIterator *aIterator, + otIp6Address *aAddress); + /** * Get the current Router ID Sequence. * diff --git a/include/openthread/types.h b/include/openthread/types.h index 6e22afb22..ba75b87d2 100644 --- a/include/openthread/types.h +++ b/include/openthread/types.h @@ -937,10 +937,12 @@ typedef struct bool mFullFunction : 1; ///< Full Function Device bool mFullNetworkData : 1; ///< Full Network Data bool mIsStateRestoring : 1; ///< Is in restoring state - uint8_t mIp6AddressesLength; ///< Number of entries in IPv6 address array. - const otIp6Address *mIp6Addresses; ///< Array of IPv6 addresses (unused entries contain unspecified address). } otChildInfo; +#define OT_CHILD_IP6_ADDRESS_ITERATOR_INIT 0 ///< Initializer for otChildIP6AddressIterator + +typedef uint16_t otChildIp6AddressIterator; ///< Used to iterate through IPv6 addresses of a Thread Child entry. + /** * This structure holds diagnostic information for a Thread Router * diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 5c8f47af0..c226f818f 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -41,6 +41,7 @@ #include "common/instance.hpp" #include "thread/mle_constants.hpp" +#include "thread/topology.hpp" using namespace ot; @@ -258,6 +259,27 @@ exit: return error; } +otError otThreadGetChildNextIp6Address(otInstance *aInstance, uint8_t aChildIndex, otChildIp6AddressIterator *aIterator, + otIp6Address *aAddress) +{ + otError error = OT_ERROR_NONE; + Instance &instance = *static_cast(aInstance); + Child::Ip6AddressIterator iterator; + Ip6::Address *address; + + VerifyOrExit(aIterator != NULL && aAddress != NULL, error = OT_ERROR_INVALID_ARGS); + + address = static_cast(aAddress); + iterator.Set(*aIterator); + + SuccessOrExit(error = instance.GetThreadNetif().GetMle().GetChildNextIp6Address(aChildIndex, iterator, *address)); + + *aIterator = iterator.Get(); + +exit: + return error; +} + uint8_t otThreadGetRouterIdSequence(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 7796aa515..131e9141f 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -47,6 +47,11 @@ using ot::Encoding::BigEndian::HostSwap32; namespace ot { namespace Ip6 { +void Address::Clear(void) +{ + memset(mFields.m8, 0, sizeof(mFields)); +} + bool Address::IsUnspecified(void) const { return (mFields.m32[0] == 0 && mFields.m32[1] == 0 && mFields.m32[2] == 0 && mFields.m32[3] == 0); diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index e7325f120..c12dd516f 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -96,6 +96,12 @@ public: kGlobalScope = 14, ///< Global scope }; + /** + * This method clears the IPv6 address by setting it to the Unspecified Address "::". + * + */ + void Clear(void); + /** * This method indicates whether or not the IPv6 address is the Unspecified Address. * diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index ad64b091f..58097e581 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -581,22 +581,23 @@ void AddressResolver::HandleAddressError(Coap::Header &aHeader, Message &aMessag for (int i = 0; i < numChildren; i++) { - if (children[i].GetState() != Neighbor::kStateValid || children[i].IsFullThreadDevice()) + Child &child = children[i]; + + if (child.GetState() != Neighbor::kStateValid || child.IsFullThreadDevice()) { continue; } - for (uint8_t j = 0; j < Child::kMaxIp6AddressPerChild; j++) + if (child.GetExtAddress() != macAddr) { - if (children[i].GetIp6Address(j) == targetTlv.GetTarget() && - children[i].GetExtAddress() != macAddr) - { - // Target EID matches child address and Mesh Local EID differs on child - memset(&children[i].GetIp6Address(j), 0, sizeof(children[i].GetIp6Address(j))); + // Mesh Local EID differs, so check whether Target EID + // matches a child address and if so remove it. + if (child.RemoveIp6Address(targetTlv.GetTarget()) == OT_ERROR_NONE) + { memset(&destination, 0, sizeof(destination)); destination.mFields.m16[0] = HostSwap16(0xfe80); - destination.SetIid(children[i].GetExtAddress()); + destination.SetIid(child.GetExtAddress()); SendAddressError(targetTlv, mlIidTlv, &destination); ExitNow(); @@ -659,22 +660,19 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag for (int i = 0; i < numChildren; i++) { - if (children[i].GetState() != Neighbor::kStateValid || - children[i].IsFullThreadDevice() || - children[i].GetLinkFailures() >= Mle::kFailedChildTransmissions) + Child &child = children[i]; + + if (child.GetState() != Neighbor::kStateValid || + child.IsFullThreadDevice() || + child.GetLinkFailures() >= Mle::kFailedChildTransmissions) { continue; } - for (uint8_t j = 0; j < Child::kMaxIp6AddressPerChild; j++) + if (child.HasIp6Address(targetTlv.GetTarget())) { - if (children[i].GetIp6Address(j) != targetTlv.GetTarget()) - { - continue; - } - - mlIidTlv.SetIid(children[i].GetExtAddress()); - lastTransactionTimeTlv.SetTime(TimerMilli::GetNow() - children[i].GetLastHeard()); + mlIidTlv.SetIid(child.GetExtAddress()); + lastTransactionTimeTlv.SetTime(TimerMilli::GetNow() - child.GetLastHeard()); SendAddressQueryResponse(targetTlv, mlIidTlv, &lastTransactionTimeTlv, aMessageInfo.GetPeerAddr()); ExitNow(); } diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 673bd685c..7922522f7 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2086,15 +2086,15 @@ otError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Chil const AddressRegistrationEntry *entry; Ip6::Address address; Lowpan::Context context; - Child *child; uint8_t count; - uint8_t index; char stringBuffer[Ip6::Address::kIp6AddressStringSize]; aChild.ClearIp6Addresses(); - for (count = 0; count < Child::kMaxIp6AddressPerChild; count++) + for (count = 0; ; count++) { + otError error = OT_ERROR_NONE; + if ((entry = aTlv.GetAddressEntry(count)) == NULL) { break; @@ -2112,10 +2112,23 @@ otError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Chil address = *entry->GetIp6Address(); } - aChild.GetIp6Address(count) = address; + // We try to accept/add as many IPv6 addresses as possible. + // "Child ID/Update Response" will indicate the accepted + // addresses. - otLogInfoMle(GetInstance(), "Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), count, - address.ToString(stringBuffer, sizeof(stringBuffer))); + error = aChild.AddIp6Address(address); + + if (error == OT_ERROR_NONE) + { + otLogInfoMle(GetInstance(), "Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), count, + address.ToString(stringBuffer, sizeof(stringBuffer))); + } + else + { + otLogWarnMle(GetInstance(), "Error %s adding IPv6 address %s to child 0x%04x", otThreadErrorToString(error), + address.ToString(stringBuffer, sizeof(stringBuffer)), aChild.GetRloc16()); + + } // We check if the same address is in-use by another child, if so // remove it. This implements "last-in wins" duplicate address @@ -2129,17 +2142,14 @@ otError MleRouter::UpdateChildAddresses(const AddressRegistrationTlv &aTlv, Chil for (int i = 0; i < mMaxChildrenAllowed; i++) { - child = &mChildren[i]; + Child &child = mChildren[i]; - if (!child->IsStateValidOrRestoring() || (child == &aChild)) + if (!child.IsStateValidOrRestoring() || (&child == &aChild)) { continue; } - if (child->FindIp6Address(address, &index) == OT_ERROR_NONE) - { - child->RemoveIp6Address(index); - } + IgnoreReturnValue(child.RemoveIp6Address(address)); } } @@ -3535,7 +3545,7 @@ Neighbor *MleRouter::GetNeighbor(const Ip6::Address &aAddress) ExitNow(rval = child); } - if (child->FindIp6Address(aAddress, NULL) == OT_ERROR_NONE) + if (child->HasIp6Address(aAddress)) { ExitNow(rval = child); } @@ -3770,6 +3780,20 @@ exit: return error; } +otError MleRouter::GetChildNextIp6Address(uint8_t aChildIndex, Child::Ip6AddressIterator &aIterator, + Ip6::Address &aAddress) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aChildIndex < mMaxChildrenAllowed, error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(mChildren[aChildIndex].IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS); + + error = mChildren[aChildIndex].GetNextIp6Address(aIterator, aAddress); + +exit: + return error; +} + void MleRouter::RestoreChildren(void) { otError error = OT_ERROR_NONE; @@ -3911,9 +3935,6 @@ otError MleRouter::GetChildInfo(Child &aChild, otChildInfo &aChildInfo) aChildInfo.mFullNetworkData = aChild.IsFullNetworkData(); aChildInfo.mIsStateRestoring = aChild.IsStateRestoring(); - aChildInfo.mIp6AddressesLength = Child::kMaxIp6AddressPerChild; - aChildInfo.mIp6Addresses = aChild.GetIp6Addresses(); - exit: return error; } @@ -4662,6 +4683,8 @@ otError MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild) { ThreadNetif &netif = GetNetif(); otError error; + Ip6::Address address; + Child::Ip6AddressIterator iterator; Tlv tlv; AddressRegistrationEntry entry; Lowpan::Context context; @@ -4671,24 +4694,19 @@ otError MleRouter::AppendChildAddresses(Message &aMessage, Child &aChild) tlv.SetType(Tlv::kAddressRegistration); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv))); - for (uint8_t i = 0; i < Child::kMaxIp6AddressPerChild; i++) + while (aChild.GetNextIp6Address(iterator, address) == OT_ERROR_NONE) { - if (aChild.GetIp6Address(i).IsUnspecified()) - { - break; - } - - if (netif.GetNetworkDataLeader().GetContext(aChild.GetIp6Address(i), context) == OT_ERROR_NONE) + if (netif.GetNetworkDataLeader().GetContext(address, context) == OT_ERROR_NONE) { // compressed entry entry.SetContextId(context.mContextId); - entry.SetIid(aChild.GetIp6Address(i).GetIid()); + entry.SetIid(address.GetIid()); } else { // uncompressed entry entry.SetUncompressed(); - entry.SetIp6Address(aChild.GetIp6Address(i)); + entry.SetIp6Address(address); } SuccessOrExit(error = aMessage.Append(&entry, entry.GetLength())); diff --git a/src/core/thread/mle_router_ftd.hpp b/src/core/thread/mle_router_ftd.hpp index 7ee644dcc..041d9b751 100644 --- a/src/core/thread/mle_router_ftd.hpp +++ b/src/core/thread/mle_router_ftd.hpp @@ -532,6 +532,21 @@ public: */ otError GetChildInfoByIndex(uint8_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(uint8_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 697de812e..dd297f43c 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -49,40 +49,96 @@ void Neighbor::GenerateChallenge(void) } } -otError Child::FindIp6Address(const Ip6::Address &aAddress, uint8_t *aIndex) const +void Child::ClearIp6Addresses(void) +{ + memset(mIp6Address, 0, sizeof(mIp6Address)); +} + +otError Child::GetNextIp6Address(Ip6AddressIterator &aIterator, Ip6::Address &aAddress) const +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aIterator.Get() < kMaxIp6AddressPerChild, error = OT_ERROR_NOT_FOUND); + VerifyOrExit(!mIp6Address[aIterator.Get()].IsUnspecified(), error = OT_ERROR_NOT_FOUND); + aAddress = mIp6Address[aIterator.Get()]; + aIterator.Increment(); + +exit: + return error; +} + +otError Child::AddIp6Address(const Ip6::Address &aAddress) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(!aAddress.IsUnspecified(), error = OT_ERROR_INVALID_ARGS); + + for (uint16_t index = 0; index < kMaxIp6AddressPerChild; index++) + { + if (mIp6Address[index].IsUnspecified()) + { + mIp6Address[index] = aAddress; + ExitNow(); + } + + VerifyOrExit(mIp6Address[index] != aAddress, error = OT_ERROR_ALREADY); + } + + error = OT_ERROR_NO_BUFS; + +exit: + return error; +} + +otError Child::RemoveIp6Address(const Ip6::Address &aAddress) { otError error = OT_ERROR_NOT_FOUND; + uint16_t index; - for (uint8_t index = 0; index < kMaxIp6AddressPerChild; index++) + VerifyOrExit(!aAddress.IsUnspecified(), error = OT_ERROR_INVALID_ARGS); + + for (index = 0; index < kMaxIp6AddressPerChild; index++) { + VerifyOrExit(!mIp6Address[index].IsUnspecified()); + if (mIp6Address[index] == aAddress) { - if (aIndex != NULL) - { - *aIndex = index; - } - error = OT_ERROR_NONE; break; } } + SuccessOrExit(error); + + for (; index < kMaxIp6AddressPerChild - 1; index++) + { + mIp6Address[index] = mIp6Address[index + 1]; + } + + mIp6Address[kMaxIp6AddressPerChild - 1].Clear(); + +exit: return error; } -void Child::RemoveIp6Address(uint8_t aIndex) +bool Child::HasIp6Address(const Ip6::Address &aAddress) const { - VerifyOrExit(aIndex < kMaxIp6AddressPerChild); + bool retval = false; - for (uint8_t i = aIndex; i < kMaxIp6AddressPerChild - 1; i++) + VerifyOrExit(!aAddress.IsUnspecified()); + + for (uint16_t index = 0; index < kMaxIp6AddressPerChild; index++) { - mIp6Address[i] = mIp6Address[i + 1]; + VerifyOrExit(!mIp6Address[index].IsUnspecified()); + + if (mIp6Address[index] == aAddress) + { + ExitNow(retval = true); + } } - memset(&mIp6Address[kMaxIp6AddressPerChild - 1], 0, sizeof(Ip6::Address)); - exit: - return; + return retval; } void Child::GenerateChallenge(void) diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index e04fbbb81..5ed5b14ed 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -361,58 +361,110 @@ class Child : public Neighbor public: enum { - kMaxIp6AddressPerChild = OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD, kMaxRequestTlvs = 5, }; /** - * This method clears the IPv6 addresses for the child. + * This class defines an iterator used by `GetNextIp6Address()` to go through IPv6 address entries of a child. * */ - void ClearIp6Addresses(void) { memset(mIp6Address, 0, sizeof(mIp6Address)); } + class Ip6AddressIterator + { + friend class Child; + + public: + + /** + * This constructor initializes the iterator object. + * + * After initialization a call to `GetNextIp6Address()` would start at the first IPv6 address entry in the list. + * + */ + Ip6AddressIterator(void): mIndex(0) { } + + /** + * This method resets the iterator. + * + * After reset the next call to `GetNextIp6Address()` would start at the first IPv6 address entry in the list. + * + */ + void Reset(void) { mIndex = 0; } + + /** + * This method sets the iterator from an `otChildIp6AddressIterator` + * + * @param[in] aChildAddressIterator A child address iterator + * + */ + void Set(otChildIp6AddressIterator aChildAddressIterator) { mIndex = aChildAddressIterator; } + + /** + * This method returns the iterator as an `otChildIp6AddressIterator` + * + * @returns The iterator as an `otChildIp6AddressIterator` + * + */ + otChildIp6AddressIterator Get(void) const { return mIndex; } + + private: + void Increment(void) { mIndex++; } + + otChildIp6AddressIterator mIndex; + }; /** - * This method gets the IPv6 address array. - * - * The array contains `kMaxIp6AddressPerChild` entries. Unused address entries contain unspecified address (all - * zeros). - * - * @returns A pointer to the first entry of IPv6 address array. + * This method clears the IPv6 address list for the child. * */ - const Ip6::Address *GetIp6Addresses(void) const { return mIp6Address; } + void ClearIp6Addresses(void); /** - * This method gets the IPv6 address at index @p aIndex. + * This method gets the next IPv6 address in the list. * - * @param[in] aIndex The index into the IPv6 address array. + * @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). * - * @returns A reference to the IPv6 address entry at index @p aIndex. + * @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. * */ - Ip6::Address &GetIp6Address(uint8_t aIndex) { return mIp6Address[aIndex]; } + otError GetNextIp6Address(Ip6AddressIterator &aIterator, Ip6::Address &aAddress) const; /** - * This method searches for a given IPv6 address in the child's IPv6 address array and provides the index of the - * address in the array if it is found. + * This method adds an IPv6 address to the list. * - * @param[in] aAddress The IPv6 address to search for in the IPv6 address array. - * @param[out] aIndex Pointer to variable where the index of address is provided if address is found in - * the array. @p aIndex can be set NULL if index is not required. + * @param[in] aAddress A reference to IPv6 address to be added. * - * @retval OT_ERROR_NONE Successfully found the address in IPv6 address array and updated @p aIndex. - * @retval OT_ERROR_NOT_FOUND Could not find the address in the array. + * @retval OT_ERROR_NONE Successfully added the new address. + * @retval OT_ERROR_ALREADY Address is already in the list. + * @retval OT_ERROR_NO_BUFS Already at maximum number of addresses. No entry available to add the new address. + * @retval OT_ERROR_INVALID_ARGS Address is invalid (it is the Unspecified Address). * */ - otError FindIp6Address(const Ip6::Address &aAddress, uint8_t *aIndex) const; + otError AddIp6Address(const Ip6::Address &aAddress); /** - * This method removes the address at index @p aIndex. + * This method removes an IPv6 address from the list. * - * @param[in] aIndex The index into the IPv6 address array. + * @param[in] aAddress A reference to IPv6 address to be removed. + * + * @retval OT_ERROR_NONE Successfully removed the address. + * @retval OT_ERROR_NOT_FOUND Address was not found in the list. + * @retval OT_ERROR_INVALID_ARGS Address is invalid (it is the Unspecified Address). * */ - void RemoveIp6Address(uint8_t aIndex); + otError RemoveIp6Address(const Ip6::Address &aAddress); + + /** + * This method indicates whether an IPv6 address is in the list of IPv6 addresses of the child. + * + * @param[in] aAddress A reference to IPv6 address. + * + * @retval TRUE The address exists on the list. + * @retval FALSE Address was not found in the list. + * + */ + bool HasIp6Address(const Ip6::Address &aAddress) const; /** * This method gets the child timeout. @@ -686,6 +738,11 @@ public: #endif // #if OPENTHREAD_ENABLE_CHILD_SUPERVISION private: + enum + { + kMaxIp6AddressPerChild = OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD, + }; + Ip6::Address mIp6Address[kMaxIp6AddressPerChild]; ///< Registered IPv6 addresses uint32_t mTimeout; ///< Child timeout diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index e73f36b7d..f887452a4 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -215,7 +215,8 @@ otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE_ADDRESSES(void) otError error = OT_ERROR_NONE; otChildInfo childInfo; uint8_t maxChildren; - const otIp6Address *ip6Address; + otIp6Address ip6Address; + otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; maxChildren = otThreadGetMaxAllowedChildren(mInstance); @@ -232,14 +233,11 @@ otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE_ADDRESSES(void) SuccessOrExit(error = mEncoder.WriteEui64(childInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(childInfo.mRloc16)); - ip6Address = childInfo.mIp6Addresses; + iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; - for (uint8_t num = childInfo.mIp6AddressesLength; num > 0; num--, ip6Address++) + while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE) { - if (!otIp6IsAddressUnspecified(ip6Address)) - { - SuccessOrExit(error = mEncoder.WriteIp6Address(*ip6Address)); - } + SuccessOrExit(error = mEncoder.WriteIp6Address(ip6Address)); } SuccessOrExit(error = mEncoder.CloseStruct()); diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 43c804f23..3cd88a558 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -83,6 +83,7 @@ endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS check_PROGRAMS = \ test-aes \ + test-child \ test-fuzz \ test-heap \ test-hmac-sha256 \ @@ -143,6 +144,9 @@ TESTS_ENVIRONMENT = \ test_aes_LDADD = $(COMMON_LDADD) test_aes_SOURCES = test_platform.cpp test_aes.cpp +test_child_LDADD = $(COMMON_LDADD) +test_child_SOURCES = test_platform.cpp test_child.cpp + test_fuzz_LDADD = $(COMMON_LDADD) test_fuzz_SOURCES = test_platform.cpp test_fuzz.cpp diff --git a/tests/unit/test_child.cpp b/tests/unit/test_child.cpp new file mode 100644 index 000000000..04ae4dc88 --- /dev/null +++ b/tests/unit/test_child.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_platform.h" + +#include +#include + +#include "common/instance.hpp" +#include "test_util.h" +#include "thread/topology.hpp" + +namespace ot { + +static ot::Instance *sInstance; + +enum +{ + kMaxChildIp6Addresses = OPENTHREAD_CONFIG_IP_ADDRS_PER_CHILD +}; + +void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, const Ip6::Address aAddressList[]) +{ + Child::Ip6AddressIterator iterator; + Ip6::Address address; + bool addressObserved[kMaxChildIp6Addresses]; + + for (uint8_t index = 0; index < aAddressListLength; index++) + { + VerifyOrQuit(aChild.HasIp6Address(aAddressList[index]), "HasIp6Address() failed\n"); + } + + memset(addressObserved, 0, sizeof(addressObserved)); + + while (aChild.GetNextIp6Address(iterator, address) == OT_ERROR_NONE) + { + bool addressIsInList = false; + + for (uint8_t index = 0; index < aAddressListLength; index++) + { + if (address == aAddressList[index]) + { + addressIsInList = true; + addressObserved[index] = true; + break; + } + } + + VerifyOrQuit(addressIsInList, "Child::GetNextIp6Address() returned an address not in the expected list\n"); + } + + for (uint8_t index = 0; index < aAddressListLength; index++) + { + VerifyOrQuit(addressObserved[index], "Child::GetNextIp6Address() missed an entry from the expected list\n"); + } +} + +void TestChildIp6Address(void) +{ + Child child; + Ip6::Address addresses[kMaxChildIp6Addresses]; + const char *ip6Addresses[] = + { + "fe80::1234", + "fd6b:e251:52fb:0:12e6:b94c:1c28:c56a", + "fd00:1234::204c:3d7c:98f6:9a1b", + "fd00:cafe::204c:3d7c:98f6:9a1b", + }; + + uint8_t numAddresses = static_cast(sizeof(ip6Addresses) / sizeof(ip6Addresses[0])); + + sInstance = testInitInstance(); + VerifyOrQuit(sInstance != NULL, "Null instance"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + printf("\nConverting IPv6 addresses from string"); + + for (uint8_t index = 0; index < numAddresses; index++) + { + VerifyOrQuit(index < kMaxChildIp6Addresses, "Too many IPv6 addresses in the unit test"); + SuccessOrQuit(addresses[index].FromString(ip6Addresses[index]), + "could not convert IPv6 address from string"); + } + + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Child state after init"); + memset(&child, 0, sizeof(child)); + VerifyChildIp6Addresses(child, 0, NULL); + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Adding a single IPv6 address"); + + for (uint8_t index = 0; index < numAddresses; index++) + { + SuccessOrQuit(child.AddIp6Address(addresses[index]), "AddIp6Address() failed"); + VerifyChildIp6Addresses(child, 1, &addresses[index]); + + child.ClearIp6Addresses(); + VerifyChildIp6Addresses(child, 0, NULL); + } + + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Adding multiple IPv6 addresses"); + + for (uint8_t index = 0; index < numAddresses; index++) + { + SuccessOrQuit(child.AddIp6Address(addresses[index]), "AddIp6Address() failed"); + VerifyChildIp6Addresses(child, index + 1, addresses); + } + + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Checking for failure when adding an address already in list"); + + for (uint8_t index = 0; index < numAddresses; index++) + { + VerifyOrQuit(child.AddIp6Address(addresses[index]) == OT_ERROR_ALREADY, + "AddIp6Address() did not fail when adding same address"); + VerifyChildIp6Addresses(child, numAddresses, addresses); + } + + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Removing addresses from list starting from front of the list"); + + for (uint8_t index = 0; index < numAddresses; index++) + { + SuccessOrQuit(child.RemoveIp6Address(addresses[index]), "RemoveIp6Address() failed"); + VerifyChildIp6Addresses(child, numAddresses - 1 - index, &addresses[index + 1]); + + VerifyOrQuit(child.RemoveIp6Address(addresses[index]) == OT_ERROR_NOT_FOUND, + "RemoveIp6Address() did not fail when removing an address not on the list"); + } + + VerifyChildIp6Addresses(child, 0, NULL); + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Removing addresses from list starting from back of the list"); + + for (uint8_t index = 0; index < numAddresses; index++) + { + SuccessOrQuit(child.AddIp6Address(addresses[index]), "AddIp6Address() failed"); + } + + for (uint8_t index = numAddresses - 1; index > 0; index--) + { + SuccessOrQuit(child.RemoveIp6Address(addresses[index]), "RemoveIp6Address() failed"); + VerifyChildIp6Addresses(child, index, &addresses[0]); + + VerifyOrQuit(child.RemoveIp6Address(addresses[index]) == OT_ERROR_NOT_FOUND, + "RemoveIp6Address() did not fail when removing an address not on the list"); + } + + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Removing address entries from middle of the list"); + + for (uint8_t indexToRemove = 1; indexToRemove < numAddresses - 1; indexToRemove++) + { + child.ClearIp6Addresses(); + + for (uint8_t index = 0; index < numAddresses; index++) + { + SuccessOrQuit(child.AddIp6Address(addresses[index]), "AddIp6Address() failed"); + } + + SuccessOrQuit(child.RemoveIp6Address(addresses[indexToRemove]), "RemoveIp6Address() failed"); + + VerifyOrQuit(child.RemoveIp6Address(addresses[indexToRemove]) == OT_ERROR_NOT_FOUND, + "RemoveIp6Address() did not fail when removing an address not on the list"); + + { + Ip6::Address updatedAddressList[kMaxChildIp6Addresses]; + uint8_t updatedListIndex = 0; + + for (uint8_t index = 0; index < numAddresses; index++) + { + if (index != indexToRemove) + { + updatedAddressList[updatedListIndex++] = addresses[index]; + } + } + + VerifyChildIp6Addresses(child, updatedListIndex, updatedAddressList); + } + } + + printf(" -- PASS\n"); + + testFreeInstance(sInstance); +} + +} // namespace ot + +#ifdef ENABLE_TEST_MAIN +int main(void) +{ + ot::TestChildIp6Address(); + printf("\nAll tests passed.\n"); + return 0; +} +#endif