diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index 5258d1fcc..f0bcdbf8b 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -124,6 +124,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index 2e4558714..288f08792 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -204,6 +204,9 @@ Source Files\thread + + Source Files\thread + Source Files\thread @@ -452,6 +455,9 @@ Header Files\thread + + Header Files\thread + Header Files\thread diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 68b2b8bbd..09f9bfe77 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -132,6 +132,7 @@ + @@ -241,6 +242,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 36caed498..d015df7b9 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -204,6 +204,9 @@ Source Files\thread + + Source Files\thread + Source Files\thread @@ -449,6 +452,9 @@ Header Files\thread + + Header Files\thread + Header Files\thread diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 8d86f072b..093467f76 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -172,6 +172,7 @@ SOURCES_COMMON = \ net/udp6.cpp \ thread/address_resolver.cpp \ thread/announce_begin_server.cpp \ + thread/child_table.cpp \ thread/data_poll_manager.cpp \ thread/energy_scan_server.cpp \ thread/key_manager.cpp \ @@ -291,6 +292,7 @@ HEADERS_COMMON = \ net/udp6.hpp \ thread/address_resolver.hpp \ thread/announce_begin_server.hpp \ + thread/child_table.hpp \ thread/data_poll_manager.hpp \ thread/energy_scan_server.hpp \ thread/key_manager.hpp \ diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index f843eaac0..b22010fb7 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -47,19 +47,16 @@ using namespace ot; uint8_t otThreadGetMaxAllowedChildren(otInstance *aInstance) { - uint8_t aNumChildren; Instance &instance = *static_cast(aInstance); - (void)instance.GetThreadNetif().GetMle().GetChildren(&aNumChildren); - - return aNumChildren; + return instance.GetThreadNetif().GetMle().GetChildTable().GetMaxChildrenAllowed(); } otError otThreadSetMaxAllowedChildren(otInstance *aInstance, uint8_t aMaxChildren) { Instance &instance = *static_cast(aInstance); - return instance.GetThreadNetif().GetMle().SetMaxAllowedChildren(aMaxChildren); + return instance.GetThreadNetif().GetMle().GetChildTable().SetMaxChildrenAllowed(aMaxChildren); } bool otThreadIsRouterRoleEnabled(otInstance *aInstance) diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 82778d169..d1cff1412 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -245,6 +245,11 @@ template <> Mle::MleRouter &Instance::Get(void) return GetThreadNetif().GetMle(); } +template <> ChildTable &Instance::Get(void) +{ + return GetThreadNetif().GetMle().GetChildTable(); +} + template <> Ip6::Netif &Instance::Get(void) { return GetThreadNetif(); diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 3f9eb406b..aa1bf3bdf 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -542,8 +542,6 @@ void AddressResolver::HandleAddressError(Coap::Header &aHeader, Message &aMessag otError error = OT_ERROR_NONE; ThreadTargetTlv targetTlv; ThreadMeshLocalEidTlv mlIidTlv; - Child * children; - uint8_t numChildren; Mac::ExtAddress macAddr; Ip6::Address destination; @@ -577,16 +575,14 @@ void AddressResolver::HandleAddressError(Coap::Header &aHeader, Message &aMessag } } - children = netif.GetMle().GetChildren(&numChildren); - memcpy(&macAddr, mlIidTlv.GetIid(), sizeof(macAddr)); macAddr.m8[0] ^= 0x2; - for (int i = 0; i < numChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance()) { - Child &child = children[i]; + Child &child = *iter.GetChild(); - if (child.GetState() != Neighbor::kStateValid || child.IsFullThreadDevice()) + if (child.IsFullThreadDevice()) { continue; } @@ -635,8 +631,6 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag ThreadTargetTlv targetTlv; ThreadMeshLocalEidTlv mlIidTlv; ThreadLastTransactionTimeTlv lastTransactionTimeTlv; - Child * children; - uint8_t numChildren; char stringBuffer[Ip6::Address::kIp6AddressStringSize]; VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_NON_CONFIRMABLE && aHeader.GetCode() == OT_COAP_CODE_POST); @@ -659,14 +653,11 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag ExitNow(); } - children = netif.GetMle().GetChildren(&numChildren); - - for (int i = 0; i < numChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance()) { - Child &child = children[i]; + Child &child = *iter.GetChild(); - if (child.GetState() != Neighbor::kStateValid || child.IsFullThreadDevice() || - child.GetLinkFailures() >= Mle::kFailedChildTransmissions) + if (child.IsFullThreadDevice() || child.GetLinkFailures() >= Mle::kFailedChildTransmissions) { continue; } diff --git a/src/core/thread/child_table.cpp b/src/core/thread/child_table.cpp new file mode 100644 index 000000000..b8ebe9f4d --- /dev/null +++ b/src/core/thread/child_table.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2016-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. + */ + +/** + * @file + * This file includes definitions for Thread child table. + */ + +#include "child_table.hpp" + +#include "common/code_utils.hpp" +#include "common/instance.hpp" + +namespace ot { + +#if OPENTHREAD_FTD + +ChildTable::Iterator::Iterator(Instance &aInstance, StateFilter aFilter) + : InstanceLocator(aInstance) + , mFilter(aFilter) + , mStart(NULL) + , mChild(NULL) +{ + Reset(); +} + +ChildTable::Iterator::Iterator(Instance &aInstance, StateFilter aFilter, Child *aStartingChild) + : InstanceLocator(aInstance) + , mFilter(aFilter) + , mStart(aStartingChild) + , mChild(NULL) +{ + Reset(); +} + +void ChildTable::Iterator::Reset(void) +{ + ChildTable &childTable = GetInstance().Get(); + + if (mStart == NULL) + { + mStart = &childTable.mChildren[0]; + } + + mChild = mStart; + + if (!MatchesFilter(*mChild, mFilter)) + { + Advance(); + } +} + +void ChildTable::Iterator::Advance(void) +{ + ChildTable &childTable = GetInstance().Get(); + Child * listStart = &childTable.mChildren[0]; + Child * listEnd = &childTable.mChildren[childTable.mMaxChildrenAllowed]; + + VerifyOrExit(mChild != NULL); + + do + { + mChild++; + + if (mChild >= listEnd) + { + mChild = listStart; + } + + VerifyOrExit(mChild != mStart, mChild = NULL); + } while (!MatchesFilter(*mChild, mFilter)); + +exit: + return; +} + +ChildTable::ChildTable(Instance &aInstance) + : InstanceLocator(aInstance) + , mMaxChildrenAllowed(kMaxChildren) +{ + memset(mChildren, 0, sizeof(mChildren)); +} + +Child *ChildTable::GetChildAtIndex(uint8_t aChildIndex) +{ + Child *child = NULL; + + VerifyOrExit(aChildIndex < mMaxChildrenAllowed); + child = &mChildren[aChildIndex]; + +exit: + return child; +} + +Child *ChildTable::GetNewChild(void) +{ + Child *child = mChildren; + + for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++) + { + if (child->GetState() == Child::kStateInvalid) + { + memset(child, 0, sizeof(Child)); + ExitNow(); + } + } + + child = NULL; + +exit: + return child; +} + +Child *ChildTable::FindChild(uint16_t aRloc16, StateFilter aFilter) +{ + Child *child = mChildren; + + for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++) + { + if (MatchesFilter(*child, aFilter) && (child->GetRloc16() == aRloc16)) + { + ExitNow(); + } + } + + child = NULL; + +exit: + return child; +} + +Child *ChildTable::FindChild(const Mac::ExtAddress &aAddress, StateFilter aFilter) +{ + Child *child = mChildren; + + for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++) + { + if (MatchesFilter(*child, aFilter) && (child->GetExtAddress() == aAddress)) + { + ExitNow(); + } + } + + child = NULL; + +exit: + return child; +} + +Child *ChildTable::FindChild(const Mac::Address &aAddress, StateFilter aFilter) +{ + Child *child = NULL; + + switch (aAddress.GetType()) + { + case Mac::Address::kTypeShort: + child = FindChild(aAddress.GetShort(), aFilter); + break; + + case Mac::Address::kTypeExtended: + child = FindChild(aAddress.GetExtended(), aFilter); + break; + + default: + break; + } + + return child; +} + +bool ChildTable::HasChildren(StateFilter aFilter) const +{ + bool rval = false; + const Child *child = mChildren; + + for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++) + { + if (MatchesFilter(*child, aFilter)) + { + ExitNow(rval = true); + } + } + +exit: + return rval; +} + +uint8_t ChildTable::GetNumChildren(StateFilter aFilter) const +{ + uint8_t numChildren = 0; + const Child *child = mChildren; + + for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++) + { + if (MatchesFilter(*child, aFilter)) + { + numChildren++; + } + } + + return numChildren; +} + +otError ChildTable::SetMaxChildrenAllowed(uint8_t aMaxChildren) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aMaxChildren > 0 && aMaxChildren <= kMaxChildren, error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(!HasChildren(kInStateAnyExceptInvalid), error = OT_ERROR_INVALID_STATE); + + mMaxChildrenAllowed = aMaxChildren; + +exit: + return error; +} + +bool ChildTable::MatchesFilter(const Child &aChild, StateFilter aFilter) +{ + bool rval = false; + + switch (aFilter) + { + case kInStateValid: + rval = (aChild.GetState() == Child::kStateValid); + break; + + case kInStateValidOrRestoring: + rval = aChild.IsStateValidOrRestoring(); + break; + + case kInStateChildIdRequest: + rval = (aChild.GetState() == Child::kStateChildIdRequest); + break; + + case kInStateValidOrAttaching: + rval = aChild.IsStateValidOrAttaching(); + break; + + case kInStateAnyExceptInvalid: + rval = (aChild.GetState() != Child::kStateInvalid); + break; + + case kInStateAnyExceptValidOrRestoing: + rval = !aChild.IsStateValidOrRestoring(); + break; + } + + return rval; +} + +#endif // OPENTHREAD_FTD + +} // namespace ot diff --git a/src/core/thread/child_table.hpp b/src/core/thread/child_table.hpp new file mode 100644 index 000000000..847860e16 --- /dev/null +++ b/src/core/thread/child_table.hpp @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2016-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. + */ + +/** + * @file + * This file includes definitions for Thread child table. + */ + +#ifndef CHILD_TABLE_HPP_ +#define CHILD_TABLE_HPP_ + +#include "openthread-core-config.h" + +#include "common/locator.hpp" +#include "thread/topology.hpp" + +namespace ot { + +#if OPENTHREAD_FTD + +/** + * This class represents the Thread child table. + * + */ +class ChildTable : public InstanceLocator +{ +public: + /** + * This enumeration defines child state filters used for finding a child or iterating through the child table. + * + * Each filter definition accepts a subset of `Child:State` values. + * + */ + enum StateFilter + { + kInStateValid, ///< Accept child only in `Child::kStateValid`. + kInStateValidOrRestoring, ///< Accept child with `Child::IsStateValidOrRestoring()` being `true`. + kInStateChildIdRequest, ///< Accept child only in `Child:kStateChildIdRequest`. + kInStateValidOrAttaching, ///< Accept child with `Child::IsStateValidOrAttaching()` being `true`. + kInStateAnyExceptInvalid, ///< Accept child in any state except `Child:kStateInvalid`. + kInStateAnyExceptValidOrRestoing, ///< Accept child in any state except `Child::IsStateValidOrRestoring()`. + }; + + /** + * This class represents an iterator for iterating through the child entries in the child table. + * + */ + class Iterator : public InstanceLocator + { + public: + /** + * This constructor initializes an `Iterator` instance to start from beginning of the child table. + * + * @param[in] aInstance A reference to the OpenThread instance. + * @param[in] aFilter A child state filter. + * + */ + Iterator(Instance &aInstance, StateFilter aFilter); + + /** + * This constructor initializes an `Iterator` instance to start from a given child. + * + * This constructor allows the iterator to start from a given `Child` entry. The iterator will start from the + * given child and will go through all entries in the child table (matching the filter) till it gets back to + * the starting `Child` entry. + * + * If the given starting `Child` pointer is `NULL`, then the iterator starts from beginning of the child table. + * + * @param[in] aInstance A reference to the OpenThread instance. + * @param[in] aFilter A child state filter. + * @param[in] aStartingChild A pointer to a child. If non-NULL, the iterator starts from the given entry. + * + */ + Iterator(Instance &aInstance, StateFilter aFilter, Child *aStartingChild); + + /** + * This method resets the iterator to start over. + * + */ + void Reset(void); + + /** + * This method indicates whether there are no more `Child` entries in the list (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 (mChild == NULL); } + + /** + * This method advances the iterator. + * + * The iterator is moved to point to the next `Child` entry matching the given state filter in the constructor. + * If there are no more `Child` entries matching the given filter, the iterator becomes empty (i.e., + * `GetChild()` returns `NULL` and `IsDone()` returns `true`. + * + */ + void Advance(void); + + /** + * This method gets the `Child` entry to which the iterator is currently pointing. + * + * @returns A pointer to the `Child` entry, or `NULL` if the iterator is done and/or empty. + * + */ + Child *GetChild(void) { return mChild; } + + private: + StateFilter mFilter; + Child * mStart; + Child * mChild; + }; + + /** + * This constructor initializes a `ChildTable` instance. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit ChildTable(Instance &aIntsance); + + /** + * This method clears the child table. + * + */ + void Clear(void) { memset(mChildren, 0, sizeof(mChildren)); } + + /** + * This method returns the child table index for a given `Child` instance. + * + * @param[in] aChild A reference to a `Child` + * + * @returns The index corresponding to @p aChild. + * + */ + uint8_t GetChildIndex(const Child &aChild) const { return static_cast(&aChild - mChildren); } + + /** + * This method returns a pointer to a `Child` entry at a given index, or `NULL` if the index is out of bounds, + * i.e., index is larger or equal to maximum number of children allowed (@sa GetMaxChildrenAllowed()). + * + * @param[in] aChildIndex A child index. + * + * @returns A pointer to the `Child` corresponding to the given index, or `NULL` if the index is out of bounds. + * + */ + Child *GetChildAtIndex(uint8_t aChildIndex); + + /** + * This method gets a new/unused `Child` entry from the child table. + * + * @note The returned child entry will be cleared (`memset` to zero). + * + * @returns A pointer to a new `Child` entry, or `NULL` if all `Child` entries are in use. + * + */ + Child *GetNewChild(void); + + /** + * This method searches the child table for a `Child` with a given RLOC16 also matching a given state filter. + * + * @param[in] aRloc16 A RLCO16 address. + * @param[in] aFilter A child state filter. + * + * @returns A pointer to the `Child` entry if one is found, or `NULL` otherwise. + * + */ + Child *FindChild(uint16_t aRloc16, StateFilter aFilter); + + /** + * This method searches the child table for a `Child` with a given extended address also matching a given state + * filter. + * + * @param[in] aAddress A reference to an extended address. + * @param[in] aFilter A child state filter. + * + * @returns A pointer to the `Child` entry if one is found, or `NULL` otherwise. + * + */ + Child *FindChild(const Mac::ExtAddress &aAddress, StateFilter aFilter); + + /** + * This method searches the child table for a `Child` with a given address also matching a given state filter. + * + * @param[in] aAddress A reference to a MAC address. + * @param[in] aFilter A child state filter. + * + * @returns A pointer to the `Child` entry if one is found, or `NULL` otherwise. + * + */ + Child *FindChild(const Mac::Address &aAddress, StateFilter aFilter); + + /** + * This method indicates whether the child table contains any child matching a given state filter. + * + * @param[in] aFilter A child state filter. + * + * @returns TRUE if the table contains at least one child table matching the given filter, FALSE otherwise. + * + */ + bool HasChildren(StateFilter aFilter) const; + + /** + * This method returns the number of children in the child table matching a given state filter. + * + * @param[in] aFilter A child state filter. + * + * @returns Number of children matching the given state filer. + * + */ + uint8_t GetNumChildren(StateFilter aFilter) const; + + /** + * This method returns the maximum number of children that can be supported (build-time constant). + * + * @note Number of children allowed (from `GetMaxChildrenAllowed()`) can be less than maximum number of supported + * children. + * + * @returns The maximum number of children supported + * + */ + uint8_t GetMaxChildren(void) const { return kMaxChildren; } + + /** + * This method get the maximum number of children allowed. + * + * @returns The maximum number of children allowed. + * + */ + uint8_t GetMaxChildrenAllowed(void) const { return mMaxChildrenAllowed; } + + /** + * This method sets the maximum number of children allowed. + * + * The number of children allowed must be at least one and at most same as maximum supported children (@sa + * GetMaxChildren()). It can be changed only if the child table is empty. + * + * @param[in] aMaxChildren Maximum number of children allowed. + * + * @retval OT_ERROR_NONE The number of allowed children changed successfully. + * @retval OT_ERROR_INVALID_ARGS If @p aMaxChildren is not in the range [1, Max supported children]. + * @retval OT_ERROR_INVALID_STATE The child table is not empty. + * + */ + otError SetMaxChildrenAllowed(uint8_t aMaxChildren); + +private: + enum + { + kMaxChildren = OPENTHREAD_CONFIG_MAX_CHILDREN, + }; + + static bool MatchesFilter(const Child &aChild, StateFilter aFilter); + + uint8_t mMaxChildrenAllowed; + Child mChildren[kMaxChildren]; +}; + +#endif // OPENTHREAD_FTD + +#if OPENTHREAD_MTD + +class ChildTable : public InstanceLocator +{ +public: + enum StateFilter + { + kInStateValid, + kInStateValidOrRestoring, + kInStateChildIdRequest, + kInStateValidOrAttaching, + kInStateAnyExceptInvalid, + }; + + class Iterator + { + public: + Iterator(Instance &, StateFilter) {} + Iterator(Instance &, StateFilter, Child *) {} + void Reset(void) {} + bool IsDone(void) const { return true; } + void Advance(void) {} + Child *GetChild(void) { return NULL; } + }; + + explicit ChildTable(Instance &aIntsance) + : InstanceLocator(aIntsance) + { + } + void Clear(void) {} + + uint8_t GetChildIndex(const Child &) const { return 0; } + Child * GetChildAtIndex(uint8_t) { return NULL; } + + Child *GetNewChild(void) { return NULL; } + + Child *FindChild(uint16_t, StateFilter) { return NULL; } + Child *FindChild(const Mac::ExtAddress &, StateFilter) { return NULL; } + Child *FindChild(const Mac::Address &, StateFilter) { return NULL; } + + bool HasChildren(StateFilter) const { return false; } + uint8_t GetNumChildren(StateFilter) const { return 0; } + uint8_t GetMaxChildren(void) const { return 0; } + uint8_t GetMaxChildrenAllowed(void) const { return 0; } + otError SetMaxChildrenAllowed(uint8_t) { return OT_ERROR_INVALID_STATE; } +}; + +#endif // OPENTHREAD_MTD + +} // namespace ot + +#endif // CHILD_TABLE_HPP_ diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index d56245cb8..d29c374be 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -121,7 +121,6 @@ otError KeyManager::SetMasterKey(const otMasterKey &aKey) { otError error = OT_ERROR_NONE; Router *routers; - Child * children; uint8_t num; VerifyOrExit(memcmp(&mMasterKey, &aKey, sizeof(mMasterKey)) != 0); @@ -147,13 +146,11 @@ otError KeyManager::SetMasterKey(const otMasterKey &aKey) } // reset child frame counters - children = GetNetif().GetMle().GetChildren(&num); - - for (uint8_t i = 0; i < num; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance()) { - children[i].SetKeySequence(0); - children[i].SetLinkFrameCounter(0); - children[i].SetMleFrameCounter(0); + iter.GetChild()->SetKeySequence(0); + iter.GetChild()->SetLinkFrameCounter(0); + iter.GetChild()->SetMleFrameCounter(0); } GetNotifier().SetFlags(OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER | OT_CHANGED_MASTER_KEY); diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 4d7db3f56..31fe40880 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -82,7 +82,7 @@ MeshForwarder::MeshForwarder(Instance &aInstance) , mSendMessageFrameCounter(0) , mSendMessageKeyId(0) , mSendMessageDataSequenceNumber(0) - , mStartChildIndex(0) + , mIndirectStartingChild(NULL) #endif , mDataPollManager(aInstance) { @@ -148,14 +148,9 @@ exit: void MeshForwarder::RemoveMessage(Message &aMessage) { - Child * children; - uint8_t numChildren; - - children = GetNetif().GetMle().GetChildren(&numChildren); - - for (uint8_t i = 0; i < numChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance()) { - IgnoreReturnValue(RemoveMessageFromSleepyChild(aMessage, children[i])); + IgnoreReturnValue(RemoveMessageFromSleepyChild(aMessage, *iter.GetChild())); } if (mSendMessage == &aMessage) @@ -569,8 +564,9 @@ otError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame) // The case where the current message requires fragmentation is // already checked and handled in `SendFragment()` method. - if (((child = netif.GetMle().GetChild(macDest)) != NULL) && !child->IsRxOnWhenIdle() && - (child->GetIndirectMessageCount() > 1)) + child = netif.GetMle().GetChildTable().FindChild(macDest, ChildTable::kInStateValidOrRestoring); + + if ((child != NULL) && !child->IsRxOnWhenIdle() && (child->GetIndirectMessageCount() > 1)) { aFrame.SetFramePending(true); } diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index f36dd98d5..76630b9b3 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -378,7 +378,7 @@ private: uint32_t mSendMessageFrameCounter; uint8_t mSendMessageKeyId; uint8_t mSendMessageDataSequenceNumber; - uint8_t mStartChildIndex; + Child * mIndirectStartingChild; #endif DataPollManager mDataPollManager; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 99b57dfe2..8315434b5 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -44,13 +44,11 @@ namespace ot { otError MeshForwarder::SendMessage(Message &aMessage) { - ThreadNetif &netif = GetNetif(); - otError error = OT_ERROR_NONE; + ThreadNetif &netif = GetNetif(); + ChildTable & childTable = netif.GetMle().GetChildTable(); + otError error = OT_ERROR_NONE; Neighbor * neighbor; - uint8_t numChildren; - Child * child; - switch (aMessage.GetType()) { case Message::kTypeIp6: @@ -73,30 +71,34 @@ otError MeshForwarder::SendMessage(Message &aMessage) if (aMessage.GetSubType() != Message::kSubTypeMplRetransmission) { - child = netif.GetMle().GetChildren(&numChildren); - if (ip6Header.GetDestination() == netif.GetMle().GetLinkLocalAllThreadNodesAddress() || ip6Header.GetDestination() == netif.GetMle().GetRealmLocalAllThreadNodesAddress()) { // destined for all sleepy children - for (uint8_t i = 0; i < numChildren; i++, child++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); + iter.Advance()) { - if (child->IsStateValidOrRestoring() && !child->IsRxOnWhenIdle()) + Child &child = *iter.GetChild(); + + if (!child.IsRxOnWhenIdle()) { - aMessage.SetChildMask(i); - mSourceMatchController.IncrementMessageCount(*child); + aMessage.SetChildMask(childTable.GetChildIndex(child)); + mSourceMatchController.IncrementMessageCount(child); } } } else { // destined for some sleepy children which subscribed the multicast address. - for (uint8_t i = 0; i < numChildren; i++, child++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); + iter.Advance()) { - if (netif.GetMle().IsSleepyChildSubscribed(ip6Header.GetDestination(), *child)) + Child &child = *iter.GetChild(); + + if (netif.GetMle().IsSleepyChildSubscribed(ip6Header.GetDestination(), child)) { - aMessage.SetChildMask(i); - mSourceMatchController.IncrementMessageCount(*child); + aMessage.SetChildMask(childTable.GetChildIndex(child)); + mSourceMatchController.IncrementMessageCount(child); } } } @@ -106,9 +108,9 @@ otError MeshForwarder::SendMessage(Message &aMessage) !neighbor->IsRxOnWhenIdle() && !aMessage.GetDirectTransmission()) { // destined for a sleepy child - child = static_cast(neighbor); - aMessage.SetChildMask(netif.GetMle().GetChildIndex(*child)); - mSourceMatchController.IncrementMessageCount(*child); + Child &child = *static_cast(neighbor); + aMessage.SetChildMask(childTable.GetChildIndex(child)); + mSourceMatchController.IncrementMessageCount(child); } else { @@ -120,13 +122,15 @@ otError MeshForwarder::SendMessage(Message &aMessage) } case Message::kTypeSupervision: - child = netif.GetChildSupervisor().GetDestination(aMessage); + { + Child *child = netif.GetChildSupervisor().GetDestination(aMessage); VerifyOrExit(child != NULL, error = OT_ERROR_DROP); VerifyOrExit(!child->IsRxOnWhenIdle(), error = OT_ERROR_DROP); - aMessage.SetChildMask(netif.GetMle().GetChildIndex(*child)); + aMessage.SetChildMask(childTable.GetChildIndex(*child)); mSourceMatchController.IncrementMessageCount(*child); break; + } default: aMessage.SetDirectTransmission(); @@ -192,7 +196,7 @@ void MeshForwarder::ClearChildIndirectMessages(Child &aChild) { nextMessage = message->GetNext(); - message->ClearChildMask(GetNetif().GetMle().GetChildIndex(aChild)); + message->ClearChildMask(GetNetif().GetMle().GetChildTable().GetChildIndex(aChild)); if (!message->IsChildPending() && !message->GetDirectTransmission()) { @@ -215,21 +219,15 @@ exit: void MeshForwarder::UpdateIndirectMessages(void) { - Child * children; - uint8_t numChildren; - - children = GetNetif().GetMle().GetChildren(&numChildren); - - for (uint8_t i = 0; i < numChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptValidOrRestoing); !iter.IsDone(); + iter.Advance()) { - Child *child = &children[i]; - - if (child->IsStateValidOrRestoring() || (child->GetIndirectMessageCount() == 0)) + if (iter.GetChild()->GetIndirectMessageCount() == 0) { continue; } - ClearChildIndirectMessages(*child); + ClearChildIndirectMessages(*iter.GetChild()); } } @@ -255,7 +253,7 @@ exit: otError MeshForwarder::RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild) { otError error = OT_ERROR_NONE; - uint8_t childIndex = GetNetif().GetMle().GetChildIndex(aChild); + uint8_t childIndex = GetNetif().GetMle().GetChildTable().GetChildIndex(aChild); VerifyOrExit(aMessage.GetChildMask(childIndex) == true, error = OT_ERROR_NOT_FOUND); @@ -350,14 +348,10 @@ void MeshForwarder::RemoveDataResponseMessages(void) if (!(ip6Header.GetDestination().IsMulticast())) { - Child * children; - uint8_t numChildren; - - children = GetNetif().GetMle().GetChildren(&numChildren); - - for (uint8_t i = 0; i < numChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); + iter.Advance()) { - IgnoreReturnValue(RemoveMessageFromSleepyChild(*message, children[i])); + IgnoreReturnValue(RemoveMessageFromSleepyChild(*message, *iter.GetChild())); } } @@ -376,32 +370,15 @@ otError MeshForwarder::GetIndirectTransmission(void) { otError error = OT_ERROR_NOT_FOUND; ThreadNetif &netif = GetNetif(); - uint8_t numChildren; - uint8_t childIndex; - uint8_t nextIndex; - Child * children; UpdateIndirectMessages(); - children = netif.GetMle().GetChildren(&numChildren); - - if (mStartChildIndex >= numChildren) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); + !iter.IsDone(); iter.Advance()) { - mStartChildIndex = 0; - } + Child &child = *iter.GetChild(); - childIndex = mStartChildIndex; - - for (uint8_t iterations = numChildren; iterations > 0; iterations--, childIndex = nextIndex) - { - Child &child = children[childIndex]; - - if ((nextIndex = childIndex + 1) == numChildren) - { - nextIndex = 0; - } - - if (!child.IsStateValidOrRestoring() || !child.IsDataRequestPending()) + if (!child.IsDataRequestPending()) { continue; } @@ -434,9 +411,9 @@ otError MeshForwarder::GetIndirectTransmission(void) child.GetMacAddress(mMacDest); } - // Record current child index, and move it to next index after this indirect transmission has completed. + // Remember the current child and move it to next one in the list after the indirect transmission has completed. - mStartChildIndex = childIndex; + mIndirectStartingChild = &child; netif.GetMac().SendFrameRequest(mMacSender); ExitNow(error = OT_ERROR_NONE); @@ -450,7 +427,7 @@ Message *MeshForwarder::GetIndirectTransmission(Child &aChild) { Message *message = NULL; Message *next; - uint8_t childIndex = GetNetif().GetMle().GetChildIndex(aChild); + uint8_t childIndex = GetNetif().GetMle().GetChildTable().GetChildIndex(aChild); for (message = mSendQueue.GetHead(); message; message = next) { @@ -569,7 +546,9 @@ void MeshForwarder::HandleDataRequest(const Mac::Address &aMacSource, const otTh VerifyOrExit(netif.GetMle().GetRole() != OT_DEVICE_ROLE_DETACHED); - VerifyOrExit((child = netif.GetMle().GetChild(aMacSource)) != NULL); + child = netif.GetMle().GetChildTable().FindChild(aMacSource, ChildTable::kInStateValidOrRestoring); + VerifyOrExit(child != NULL); + child->SetLastHeard(TimerMilli::GetNow()); child->ResetLinkFailures(); indirectMsgCount = child->GetIndirectMessageCount(); @@ -593,7 +572,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr ThreadNetif &netif = GetNetif(); Child * child; - child = netif.GetMle().GetChild(aMacDest); + child = netif.GetMle().GetChildTable().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring); VerifyOrExit(child != NULL); child->SetDataRequestPending(false); @@ -604,12 +583,14 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr { // To ensure fairness in handling of data requests from sleepy // children, once a message is completed for indirect transmission to a - // child (no matter succeed or failed), the `mStartChildIndex` is updated to - // the next index after the current child. Subsequent call to - // `ScheduleTransmissionTask()` will begin the iteration through - // the children list from this index. + // child (on both success or failure), the `mIndirectStartingChild` is + // updated to the next `Child` entry after the current one. Subsequent + // call to `ScheduleTransmissionTask()` will begin the iteration + // through the children list from this child. - mStartChildIndex++; + ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); + iter.Advance(); + mIndirectStartingChild = iter.GetChild(); if (aError == OT_ERROR_NONE) { @@ -685,7 +666,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr mSourceMatchController.SetSrcMatchAsShort(*child, true); } - childIndex = netif.GetMle().GetChildIndex(*child); + childIndex = netif.GetMle().GetChildTable().GetChildIndex(*child); if (mSendMessage->GetChildMask(childIndex)) { diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 0e2a30f64..b8039c4c9 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -64,7 +64,7 @@ MleRouter::MleRouter(Instance &aInstance) , mAddressRelease(OT_URI_PATH_ADDRESS_RELEASE, &MleRouter::HandleAddressRelease, this) , mRouterIdSequence(0) , mRouterIdSequenceLastUpdated(0) - , mMaxChildrenAllowed(kMaxChildren) + , mChildTable(aInstance) , mChildTableChangedCallback(NULL) , mChallengeTimeout(0) , mNextChildId(kMaxChildId) @@ -82,7 +82,6 @@ MleRouter::MleRouter(Instance &aInstance) { mDeviceMode |= ModeTlv::kModeFFD | ModeTlv::kModeFullNetworkData; - memset(mChildren, 0, sizeof(mChildren)); memset(mRouters, 0, sizeof(mRouters)); SetRouterId(kInvalidRouterId); @@ -448,11 +447,11 @@ otError MleRouter::SetStateRouter(uint16_t aRloc16) } // remove children that do not have matching RLOC16 - for (int i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance()) { - if (mChildren[i].IsStateValidOrRestoring() && GetRouterId(mChildren[i].GetRloc16()) != mRouterId) + if (GetRouterId(iter.GetChild()->GetRloc16()) != mRouterId) { - RemoveNeighbor(mChildren[i]); + RemoveNeighbor(*iter.GetChild()); } } @@ -495,11 +494,11 @@ otError MleRouter::SetStateLeader(uint16_t aRloc16) netif.GetAddressResolver().Clear(); // remove children that do not have matching RLOC16 - for (int i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance()) { - if (mChildren[i].IsStateValidOrRestoring() && GetRouterId(mChildren[i].GetRloc16()) != mRouterId) + if (GetRouterId(iter.GetChild()->GetRloc16()) != mRouterId) { - RemoveNeighbor(mChildren[i]); + RemoveNeighbor(*iter.GetChild()); } } @@ -1104,51 +1103,6 @@ exit: return error; } -Child *MleRouter::NewChild(void) -{ - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].GetState() == Neighbor::kStateInvalid) - { - return &mChildren[i]; - } - } - - return NULL; -} - -Child *MleRouter::FindChild(uint16_t aChildId) -{ - Child *rval = NULL; - - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].GetState() != Neighbor::kStateInvalid && GetChildId(mChildren[i].GetRloc16()) == aChildId) - { - ExitNow(rval = &mChildren[i]); - } - } - -exit: - return rval; -} - -Child *MleRouter::FindChild(const Mac::ExtAddress &aAddress) -{ - Child *rval = NULL; - - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].GetState() != Neighbor::kStateInvalid && mChildren[i].GetExtAddress() == aAddress) - { - ExitNow(rval = &mChildren[i]); - } - } - -exit: - return rval; -} - uint8_t MleRouter::LinkQualityToCost(uint8_t aLinkQuality) { switch (aLinkQuality) @@ -1762,11 +1716,11 @@ otError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::Messa SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge)); VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE); - child = FindChild(macAddr); + child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid); if (child == NULL) { - VerifyOrExit((child = NewChild()) != NULL); + VerifyOrExit((child = GetChildTable().GetNewChild()) != NULL); memset(child, 0, sizeof(*child)); @@ -1897,11 +1851,12 @@ void MleRouter::HandleStateUpdateTimer(void) } // update children state - for (int i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance()) { + Child & child = *iter.GetChild(); uint32_t timeout = 0; - switch (mChildren[i].GetState()) + switch (child.GetState()) { case Neighbor::kStateInvalid: case Neighbor::kStateChildIdRequest: @@ -1911,7 +1866,7 @@ void MleRouter::HandleStateUpdateTimer(void) case Neighbor::kStateValid: case Neighbor::kStateRestored: case Neighbor::kStateChildUpdateRequest: - timeout = TimerMilli::SecToMsec(mChildren[i].GetTimeout()); + timeout = TimerMilli::SecToMsec(child.GetTimeout()); break; case Neighbor::kStateParentResponse: @@ -1920,15 +1875,15 @@ void MleRouter::HandleStateUpdateTimer(void) break; } - if ((TimerMilli::GetNow() - mChildren[i].GetLastHeard()) >= timeout) + if ((TimerMilli::GetNow() - child.GetLastHeard()) >= timeout) { otLogInfoMle(GetInstance(), "Child timeout expired"); - RemoveNeighbor(mChildren[i]); + RemoveNeighbor(child); } else if ((mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER) && - (mChildren[i].GetState() == Neighbor::kStateRestored)) + (child.GetState() == Neighbor::kStateRestored)) { - SendChildUpdateRequest(mChildren[i]); + SendChildUpdateRequest(child); } } @@ -2140,16 +2095,15 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse // 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++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); + iter.Advance()) { - Child &child = mChildren[i]; - - if (!child.IsStateValidOrRestoring() || (&child == &aChild)) + if (iter.GetChild() == &aChild) { continue; } - IgnoreReturnValue(child.RemoveIp6Address(GetInstance(), address)); + IgnoreReturnValue(iter.GetChild()->RemoveIp6Address(GetInstance(), address)); } } @@ -2202,7 +2156,8 @@ otError MleRouter::HandleChildIdRequest(const Message & aMessage, // Find Child aMessageInfo.GetPeerAddr().ToExtAddress(macAddr); - VerifyOrExit((child = FindChild(macAddr)) != NULL, error = OT_ERROR_ALREADY); + child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid); + VerifyOrExit(child != NULL, error = OT_ERROR_ALREADY); // Response SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response)); @@ -2364,7 +2319,7 @@ otError MleRouter::HandleChildUpdateRequest(const Message & aMessage, // Find Child aMessageInfo.GetPeerAddr().ToExtAddress(macAddr); - child = FindChild(macAddr); + child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid); tlvs[tlvslength++] = Tlv::kSourceAddress; @@ -2471,7 +2426,7 @@ otError MleRouter::HandleChildUpdateResponse(const Message & aMessage, // Find Child aMessageInfo.GetPeerAddr().ToExtAddress(macAddr); - child = FindChild(macAddr); + child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid); if (child == NULL) { @@ -2644,12 +2599,12 @@ void MleRouter::SynchronizeChildNetworkData(void) VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER); - for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance()) { - Child & child = mChildren[i]; + Child & child = *iter.GetChild(); uint8_t version; - if (child.GetState() != Neighbor::kStateValid || child.IsRxOnWhenIdle()) + if (child.IsRxOnWhenIdle()) { continue; } @@ -2907,7 +2862,7 @@ otError MleRouter::SendChildIdResponse(Child &aChild) { mNextChildId = kMinChildId; } - } while (FindChild(mNextChildId) != NULL); + } while (GetChildTable().FindChild(mNextChildId, ChildTable::kInStateAnyExceptInvalid) != NULL); // allocate Child ID aChild.SetRloc16(netif.GetMac().GetShortAddress() | mNextChildId); @@ -2979,7 +2934,7 @@ otError MleRouter::SendChildUpdateRequest(Child &aChild) if (!aChild.IsRxOnWhenIdle()) { - uint8_t childIndex = netif.GetMle().GetChildIndex(aChild); + uint8_t childIndex = netif.GetMle().GetChildTable().GetChildIndex(aChild); for (message = netif.GetMeshForwarder().GetSendQueue().GetHead(); message; message = message->GetNext()) { @@ -3190,64 +3145,6 @@ exit: return error; } -Child *MleRouter::GetChild(uint16_t aAddress) -{ - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetRloc16() == aAddress) - { - return &mChildren[i]; - } - } - - return NULL; -} - -Child *MleRouter::GetChild(const Mac::ExtAddress &aAddress) -{ - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetExtAddress() == aAddress) - { - return &mChildren[i]; - } - } - - return NULL; -} - -Child *MleRouter::GetChild(const Mac::Address &aAddress) -{ - switch (aAddress.GetType()) - { - case Mac::Address::kTypeShort: - return GetChild(aAddress.GetShort()); - - case Mac::Address::kTypeExtended: - return GetChild(aAddress.GetExtended()); - - default: - break; - } - - return NULL; -} - -uint8_t MleRouter::GetChildIndex(const Child &aChild) -{ - return static_cast(&aChild - mChildren); -} - -Child *MleRouter::GetChildren(uint8_t *aNumChildren) -{ - if (aNumChildren != NULL) - { - *aNumChildren = mMaxChildrenAllowed; - } - - return mChildren; -} - bool MleRouter::IsMinimalChild(uint16_t aRloc16) { ThreadNetif &netif = GetNetif(); @@ -3265,23 +3162,6 @@ bool MleRouter::IsMinimalChild(uint16_t aRloc16) return rval; } -otError MleRouter::SetMaxAllowedChildren(uint8_t aMaxChildren) -{ - otError error = OT_ERROR_NONE; - - // Ensure the value is between 1 and kMaxChildren - VerifyOrExit(aMaxChildren > 0 && aMaxChildren <= kMaxChildren, error = OT_ERROR_INVALID_ARGS); - - // Do not allow setting max children if MLE is running - VerifyOrExit(mRole == OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE); - - // Save the value - mMaxChildrenAllowed = aMaxChildren; - -exit: - return error; -} - otError MleRouter::RemoveNeighbor(const Mac::Address &aAddress) { otError error = OT_ERROR_NONE; @@ -3394,13 +3274,8 @@ Neighbor *MleRouter::GetNeighbor(uint16_t aAddress) case OT_DEVICE_ROLE_ROUTER: case OT_DEVICE_ROLE_LEADER: - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetRloc16() == aAddress) - { - ExitNow(rval = &mChildren[i]); - } - } + rval = GetChildTable().FindChild(aAddress, ChildTable::kInStateValidOrRestoring); + VerifyOrExit(rval == NULL); if (IsActiveRouter(aAddress)) { @@ -3436,13 +3311,8 @@ Neighbor *MleRouter::GetNeighbor(const Mac::ExtAddress &aAddress) case OT_DEVICE_ROLE_ROUTER: case OT_DEVICE_ROLE_LEADER: - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetExtAddress() == aAddress) - { - ExitNow(rval = &mChildren[i]); - } - } + rval = GetChildTable().FindChild(aAddress, ChildTable::kInStateValidOrRestoring); + VerifyOrExit(rval == NULL); for (int i = 0; i <= kMaxRouterId; i++) { @@ -3518,14 +3388,9 @@ Neighbor *MleRouter::GetNeighbor(const Ip6::Address &aAddress) context.mContextId = 0xff; } - for (int i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance()) { - child = &mChildren[i]; - - if (!child->IsStateValidOrRestoring()) - { - continue; - } + child = iter.GetChild(); if (context.mContextId == 0 && aAddress.mFields.m16[4] == HostSwap16(0x0000) && aAddress.mFields.m16[5] == HostSwap16(0x00ff) && aAddress.mFields.m16[6] == HostSwap16(0xfe00) && @@ -3748,7 +3613,9 @@ otError MleRouter::GetChildInfoById(uint16_t aChildId, otChildInfo &aChildInfo) aChildId = GetChildId(aChildId); } - VerifyOrExit((child = FindChild(aChildId)) != NULL, error = OT_ERROR_NOT_FOUND); + child = GetChildTable().FindChild(aChildId, ChildTable::kInStateAnyExceptInvalid); + VerifyOrExit(child != NULL, error = OT_ERROR_NOT_FOUND); + error = GetChildInfo(*child, aChildInfo); exit: @@ -3758,9 +3625,12 @@ exit: otError MleRouter::GetChildInfoByIndex(uint8_t aChildIndex, otChildInfo &aChildInfo) { otError error = OT_ERROR_NONE; + Child * child = NULL; - VerifyOrExit(aChildIndex < mMaxChildrenAllowed, error = OT_ERROR_INVALID_ARGS); - error = GetChildInfo(mChildren[aChildIndex], aChildInfo); + child = GetChildTable().GetChildAtIndex(aChildIndex); + VerifyOrExit(child != NULL, error = OT_ERROR_NOT_FOUND); + + error = GetChildInfo(*child, aChildInfo); exit: return error; @@ -3771,11 +3641,13 @@ otError MleRouter::GetChildNextIp6Address(uint8_t aChildIndex Ip6::Address & aAddress) { otError error = OT_ERROR_NONE; + Child * child = NULL; - VerifyOrExit(aChildIndex < mMaxChildrenAllowed, error = OT_ERROR_INVALID_ARGS); - VerifyOrExit(mChildren[aChildIndex].IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS); + child = GetChildTable().GetChildAtIndex(aChildIndex); + VerifyOrExit(child != NULL, error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(child->IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS); - error = mChildren[aChildIndex].GetNextIp6Address(GetInstance(), aIterator, aAddress); + error = child->GetNextIp6Address(GetInstance(), aIterator, aAddress); exit: return error; @@ -3792,11 +3664,12 @@ void MleRouter::RestoreChildren(void) Child * child; const Settings::ChildInfo &childInfo = iter.GetChildInfo(); - child = FindChild(*static_cast(&childInfo.mExtAddress)); + child = GetChildTable().FindChild(*static_cast(&childInfo.mExtAddress), + ChildTable::kInStateAnyExceptInvalid); if (child == NULL) { - VerifyOrExit((child = NewChild()) != NULL, error = OT_ERROR_NO_BUFS); + VerifyOrExit((child = GetChildTable().GetNewChild()) != NULL, error = OT_ERROR_NO_BUFS); } else { @@ -3817,7 +3690,7 @@ void MleRouter::RestoreChildren(void) exit: - if (foundDuplicate || (numChildren > kMaxChildren) || (error != OT_ERROR_NONE)) + if (foundDuplicate || (numChildren > GetChildTable().GetMaxChildren()) || (error != OT_ERROR_NONE)) { // If there is any error, e.g., there are more saved children // in non-volatile settings than could be restored or there are @@ -3852,7 +3725,8 @@ otError MleRouter::StoreChild(uint16_t aChildRloc16) Child * child; Settings::ChildInfo childInfo; - VerifyOrExit((child = FindChild(GetChildId(aChildRloc16))) != NULL, error = OT_ERROR_NOT_FOUND); + child = GetChildTable().FindChild(GetChildId(aChildRloc16), ChildTable::kInStateAnyExceptInvalid); + VerifyOrExit(child != NULL, error = OT_ERROR_NOT_FOUND); IgnoreReturnValue(RemoveStoredChild(aChildRloc16)); @@ -3874,12 +3748,9 @@ otError MleRouter::RefreshStoredChildren(void) SuccessOrExit(error = GetInstance().GetSettings().DeleteChildInfo()); - for (uint8_t i = 0; i < kMaxChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance()) { - if (mChildren[i].GetState() != Neighbor::kStateInvalid) - { - SuccessOrExit(error = StoreChild(mChildren[i].GetRloc16())); - } + SuccessOrExit(error = StoreChild(iter.GetChild()->GetRloc16())); } exit: @@ -3959,15 +3830,22 @@ otError MleRouter::GetNextNeighborInfo(otNeighborInfoIterator &aIterator, otNeig memset(&aNeighInfo, 0, sizeof(aNeighInfo)); - // Non-negative iterator value gives the current index into mChildren array + // Non-negative iterator value gives the Child index into child table if (aIterator >= 0) { - for (index = aIterator; index < mMaxChildrenAllowed; index++) + for (index = aIterator;; index++) { - if (mChildren[index].GetState() == Neighbor::kStateValid) + Child *child = GetChildTable().GetChildAtIndex(static_cast(index)); + + if (child == NULL) { - neighbor = &mChildren[index]; + break; + } + + if (child->GetState() == Neighbor::kStateValid) + { + neighbor = child; aNeighInfo.mIsChild = true; index++; aIterator = index; @@ -4068,7 +3946,7 @@ otError MleRouter::CheckReachability(uint16_t aMeshSource, uint16_t aMeshDest, I else if (GetRouterId(aMeshDest) == mRouterId) { // mesh destination is a child of this device - if (GetChild(aMeshDest)) + if (GetChildTable().FindChild(aMeshDest, ChildTable::kInStateValidOrRestoring)) { return OT_ERROR_NONE; } @@ -4294,26 +4172,9 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Header * aHeader, SendLinkRequest(NULL); // send child id responses - for (int i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateChildIdRequest); !iter.IsDone(); iter.Advance()) { - switch (mChildren[i].GetState()) - { - case Neighbor::kStateChildIdRequest: - SendChildIdResponse(mChildren[i]); - break; - - case Neighbor::kStateParentResponse: - case Neighbor::kStateLinkRequest: - assert(false); - break; - - case Neighbor::kStateInvalid: - case Neighbor::kStateParentRequest: - case Neighbor::kStateValid: - case Neighbor::kStateRestored: - case Neighbor::kStateChildUpdateRequest: - break; - } + SendChildIdResponse(*iter.GetChild()); } exit: @@ -4535,7 +4396,6 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) ConnectivityTlv &tlv = aTlv; uint8_t cost; uint8_t linkQuality; - uint8_t numChildren = 0; int8_t parentPriority = kParentPriorityMedium; if (mParentPriority != kParentPriorityUnspecified) @@ -4544,15 +4404,10 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) } else { - for (int i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].GetState() == Neighbor::kStateValid) - { - numChildren++; - } - } + uint8_t numChildren = GetChildTable().GetNumChildren(ChildTable::kInStateValid); + uint8_t maxAllowed = GetChildTable().GetMaxChildrenAllowed(); - if ((mMaxChildrenAllowed - numChildren) < (mMaxChildrenAllowed / 3)) + if ((maxAllowed - numChildren) < (maxAllowed / 3)) { parentPriority = kParentPriorityLow; } @@ -4877,29 +4732,14 @@ exit: bool MleRouter::HasChildren(void) { - bool hasChildren = false; - - for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].GetState() == Neighbor::kStateRestored || - mChildren[i].GetState() >= Neighbor::kStateChildIdRequest) - { - ExitNow(hasChildren = true); - } - } - -exit: - return hasChildren; + return GetChildTable().HasChildren(ChildTable::kInStateValidOrAttaching); } void MleRouter::RemoveChildren(void) { - for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance()) { - if (mChildren[i].IsStateValidOrRestoring()) - { - RemoveNeighbor(mChildren[i]); - } + RemoveNeighbor(*iter.GetChild()); } } @@ -4910,13 +4750,7 @@ bool MleRouter::HasSmallNumberOfChildren(void) VerifyOrExit(routerCount > mRouterDowngradeThreshold); - for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) - { - if (mChildren[i].GetState() == Neighbor::kStateValid) - { - numChildren++; - } - } + numChildren = GetChildTable().GetNumChildren(ChildTable::kInStateValid); return numChildren < (routerCount - mRouterDowngradeThreshold) * 3; @@ -4976,21 +4810,18 @@ otError MleRouter::GetMaxChildTimeout(uint32_t &aTimeout) const VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER, error = OT_ERROR_INVALID_STATE); - for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance()) { - if (mChildren[i].GetState() != Neighbor::kStateValid) + Child &child = *iter.GetChild(); + + if (child.IsFullThreadDevice()) { continue; } - if (mChildren[i].IsFullThreadDevice()) + if (child.GetTimeout() > aTimeout) { - continue; - } - - if (mChildren[i].GetTimeout() > aTimeout) - { - aTimeout = mChildren[i].GetTimeout(); + aTimeout = child.GetTimeout(); } error = OT_ERROR_NONE; @@ -5029,14 +4860,16 @@ bool MleRouter::HasSleepyChildrenSubscribed(const Ip6::Address &aAddress) { bool rval = false; - for (uint8_t i = 0; i < mMaxChildrenAllowed; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance()) { - if (!mChildren[i].IsStateValidOrRestoring() || mChildren[i].IsRxOnWhenIdle()) + Child &child = *iter.GetChild(); + + if (child.IsRxOnWhenIdle()) { continue; } - if (IsSleepyChildSubscribed(aAddress, mChildren[i])) + if (IsSleepyChildSubscribed(aAddress, child)) { ExitNow(rval = true); } diff --git a/src/core/thread/mle_router_ftd.hpp b/src/core/thread/mle_router_ftd.hpp index f3b3cc6d1..a6cf85c8e 100644 --- a/src/core/thread/mle_router_ftd.hpp +++ b/src/core/thread/mle_router_ftd.hpp @@ -48,6 +48,7 @@ #include "meshcop/meshcop_tlvs.hpp" #include "net/icmp6.hpp" #include "net/udp6.hpp" +#include "thread/child_table.hpp" #include "thread/mle.hpp" #include "thread/mle_tlvs.hpp" #include "thread/thread_tlvs.hpp" @@ -378,66 +379,12 @@ public: otError RemoveNeighbor(Neighbor &aNeighbor); /** - * This method returns a pointer to a Child object. + * This method gets the `ChildTable` object. * - * @param[in] aAddress The address of the Child. - * - * @returns A pointer to the Child object. + * @returns A reference to the `ChildTable`. * */ - Child *GetChild(uint16_t aAddress); - - /** - * This method returns a pointer to a Child object. - * - * @param[in] aAddress A reference to the address of the Child. - * - * @returns A pointer to the Child object. - * - */ - Child *GetChild(const Mac::ExtAddress &aAddress); - - /** - * This method returns a pointer to a Child object. - * - * @param[in] aAddress A reference to the address of the Child. - * - * @returns A pointer to the Child corresponding to @p aAddress, NULL otherwise. - * - */ - Child *GetChild(const Mac::Address &aAddress); - - /** - * This method returns a child index for the Child object. - * - * @param[in] aChild A reference to the Child object. - * - * @returns The index for the Child corresponding to @p aChild. - * - */ - uint8_t GetChildIndex(const Child &aChild); - - /** - * This method returns a pointer to a Child array. - * - * @param[out] aNumChildren A pointer to output the number of children. - * - * @returns A pointer to the Child array. - * - */ - Child *GetChildren(uint8_t *aNumChildren); - - /** - * This method sets the max children allowed value for this Thread interface. - * - * @param[in] aMaxChildren The max children allowed value. - * - * @retval OT_ERROR_NONE Successfully set the max. - * @retval OT_ERROR_INVALID_ARGS If @p aMaxChildren is not in the range [1, kMaxChildren]. - * @retval OT_ERROR_INVALID_STATE If MLE has already been started. - * - */ - otError SetMaxAllowedChildren(uint8_t aMaxChildren); + ChildTable &GetChildTable(void) { return mChildTable; } /** * This method restores children information from non-volatile memory. @@ -882,10 +829,6 @@ private: void HandlePartitionChange(void); - Child *NewChild(void); - Child *FindChild(uint16_t aChildId); - Child *FindChild(const Mac::ExtAddress &aMacAddr); - void SetChildStateToValid(Child &aChild); bool HasChildren(void); void RemoveChildren(void); @@ -914,8 +857,8 @@ private: uint8_t mRouterIdSequence; uint32_t mRouterIdSequenceLastUpdated; Router mRouters[kMaxRouterId + 1]; - uint8_t mMaxChildrenAllowed; - Child mChildren[kMaxChildren]; + + ChildTable mChildTable; otThreadChildTableCallback mChildTableChangedCallback; diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp index 37e1ed61d..549185d78 100644 --- a/src/core/thread/mle_router_mtd.hpp +++ b/src/core/thread/mle_router_mtd.hpp @@ -38,6 +38,7 @@ #include "utils/wrap_string.h" +#include "thread/child_table.hpp" #include "thread/mle.hpp" #include "thread/mle_tlvs.hpp" #include "thread/thread_tlvs.hpp" @@ -52,6 +53,7 @@ class MleRouter : public Mle public: explicit MleRouter(Instance &aInstance) : Mle(aInstance) + , mChildTable(aInstance) { } @@ -83,21 +85,7 @@ public: otError RemoveNeighbor(const Mac::Address &) { return BecomeDetached(); } otError RemoveNeighbor(Neighbor &) { return BecomeDetached(); } - Child *GetChild(uint16_t) { return NULL; } - Child *GetChild(const Mac::ExtAddress &) { return NULL; } - Child *GetChild(const Mac::Address &) { return NULL; } - - uint8_t GetChildIndex(const Child &) { return 0; } - - Child *GetChildren(uint8_t *aNumChildren) - { - if (aNumChildren != NULL) - { - *aNumChildren = 0; - } - - return NULL; - } + ChildTable &GetChildTable(void) { return mChildTable; } bool IsMinimalChild(uint16_t) { return false; } @@ -176,6 +164,8 @@ private: OT_UNUSED_VARIABLE(aRoute); return OT_ERROR_NONE; } + + ChildTable mChildTable; }; } // namespace Mle diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index ae03d2ebe..c5af127cc 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -235,43 +235,33 @@ otError NetworkDiagnostic::AppendChildTable(Message &aMessage) otError error = OT_ERROR_NONE; uint8_t count = 0; uint8_t timeout = 0; - uint8_t numChildren; - const Child * children = netif.GetMle().GetChildren(&numChildren); ChildTableTlv tlv; ChildTableEntry entry; tlv.Init(); - for (int i = 0; i < numChildren; i++) - { - if (children[i].GetState() == Neighbor::kStateValid) - { - count++; - } - } - + count = netif.GetMle().GetChildTable().GetNumChildren(ChildTable::kInStateValid); tlv.SetLength(count * sizeof(ChildTableEntry)); SuccessOrExit(error = aMessage.Append(&tlv, sizeof(ChildTableTlv))); - for (int i = 0; i < numChildren; i++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance()) { - if (children[i].GetState() == Neighbor::kStateValid) + Child &child = *iter.GetChild(); + + timeout = 0; + + while (static_cast(1 << timeout) < child.GetTimeout()) { - timeout = 0; - - while (static_cast(1 << timeout) < children[i].GetTimeout()) - { - timeout++; - } - - entry.SetReserved(0); - entry.SetTimeout(timeout + 4); - entry.SetChildId(netif.GetMle().GetChildId(children[i].GetRloc16())); - entry.SetMode(children[i].GetDeviceMode()); - - SuccessOrExit(error = aMessage.Append(&entry, sizeof(ChildTableEntry))); + timeout++; } + + entry.SetReserved(0); + entry.SetTimeout(timeout + 4); + entry.SetChildId(netif.GetMle().GetChildId(child.GetRloc16())); + entry.SetMode(child.GetDeviceMode()); + + SuccessOrExit(error = aMessage.Append(&entry, sizeof(ChildTableEntry))); } exit: diff --git a/src/core/thread/src_match_controller.cpp b/src/core/thread/src_match_controller.cpp index a983bba97..ea692b20f 100644 --- a/src/core/thread/src_match_controller.cpp +++ b/src/core/thread/src_match_controller.cpp @@ -218,17 +218,13 @@ exit: otError SourceMatchController::AddPendingEntries(void) { otError error = OT_ERROR_NONE; - uint8_t numChildren; - Child * child; - child = GetNetif().GetMle().GetChildren(&numChildren); - - for (uint8_t i = 0; i < numChildren; i++, child++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance()) { - if (child->IsStateValidOrRestoring() && child->IsIndirectSourceMatchPending()) + if (iter.GetChild()->IsIndirectSourceMatchPending()) { - SuccessOrExit(error = AddAddress(*child)); - child->SetIndirectSourceMatchPending(false); + SuccessOrExit(error = AddAddress(*iter.GetChild())); + iter.GetChild()->SetIndirectSourceMatchPending(false); } } diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index a43eddb56..b4417f41d 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -47,6 +47,29 @@ void Neighbor::GenerateChallenge(void) Random::FillBuffer(mValidPending.mPending.mChallenge, sizeof(mValidPending.mPending.mChallenge)); } +bool Child::IsStateValidOrAttaching(void) const +{ + bool rval = false; + + switch (GetState()) + { + case kStateInvalid: + case kStateParentRequest: + case kStateParentResponse: + break; + + case kStateRestored: + case kStateChildIdRequest: + case kStateLinkRequest: + case kStateChildUpdateRequest: + case kStateValid: + rval = true; + break; + } + + return rval; +} + void Child::ClearIp6Addresses(void) { memset(mMeshLocalIid, 0, sizeof(mMeshLocalIid)); diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 46d98a779..23630cbc5 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -87,7 +87,7 @@ public: void SetState(State aState) { mState = static_cast(aState); } /** - * Check if the neighbor/child is being restored. + * This method indicates whether the neighbor/child is being restored. * * @returns TRUE if the neighbor is being restored, FALSE otherwise. * @@ -95,7 +95,8 @@ public: bool IsStateRestoring(void) const { return (mState == kStateRestored) || (mState == kStateChildUpdateRequest); } /** - * Check if the neighbor/child is in valid state or if it is being restored. + * This method indicates whether 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. @@ -414,6 +415,17 @@ public: otChildIp6AddressIterator mIndex; }; + /** + * This method indicates if the child state is valid or being attached or being restored. + * + * The states `kStateRestored`, `kStateChildIdRequest`, `kStateChildUpdateRequest`, `kStateValid`, (and + * `kStateLinkRequest) are considered as attached or being restored. + * + * @returns TRUE if the child is attached or being restored. + * + */ + bool IsStateValidOrAttaching(void) const; + /** * This method clears the IPv6 address list for the child. * diff --git a/src/core/utils/child_supervision.cpp b/src/core/utils/child_supervision.cpp index f8326b5ce..49dec21b9 100644 --- a/src/core/utils/child_supervision.cpp +++ b/src/core/utils/child_supervision.cpp @@ -68,14 +68,11 @@ Child *ChildSupervisor::GetDestination(const Message &aMessage) const { Child * child = NULL; uint8_t childIndex; - uint8_t numChildren; VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision); aMessage.Read(0, sizeof(childIndex), &childIndex); - child = GetNetif().GetMle().GetChildren(&numChildren); - VerifyOrExit(childIndex < numChildren, child = NULL); - child += childIndex; + child = GetNetif().GetMle().GetChildTable().GetChildAtIndex(childIndex); exit: return child; @@ -97,7 +94,7 @@ void ChildSupervisor::SendMessage(Child &aChild) // the destination of the message to be later retrieved using // `ChildSupervisor::GetDestination(message)`. - childIndex = netif.GetMle().GetChildIndex(aChild); + childIndex = netif.GetMle().GetChildTable().GetChildIndex(aChild); SuccessOrExit(message->Append(&childIndex, sizeof(childIndex))); SuccessOrExit(netif.SendMessage(*message)); @@ -125,25 +122,17 @@ void ChildSupervisor::HandleTimer(Timer &aTimer) void ChildSupervisor::HandleTimer(void) { - Child * child; - uint8_t numChildren; - VerifyOrExit(mSupervisionInterval != 0); - child = GetNetif().GetMle().GetChildren(&numChildren); - - for (uint8_t i = 0; i < numChildren; i++, child++) + for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance()) { - if (child->GetState() != Child::kStateValid) - { - continue; - } + Child &child = *iter.GetChild(); - child->IncrementSecondsSinceLastSupervision(); + child.IncrementSecondsSinceLastSupervision(); - if ((child->GetSecondsSinceLastSupervision() >= mSupervisionInterval) && (child->IsRxOnWhenIdle() == false)) + if ((child.GetSecondsSinceLastSupervision() >= mSupervisionInterval) && (child.IsRxOnWhenIdle() == false)) { - SendMessage(*child); + SendMessage(child); } } @@ -155,23 +144,15 @@ exit: void ChildSupervisor::CheckState(void) { - bool shouldRun = false; - uint8_t numChildren = 0; - Mle::MleRouter &mle = GetInstance().Get(); + bool shouldRun = false; + Mle::MleRouter &mle = GetInstance().Get(); // Child Supervision should run if `mSupervisionInterval` is not // zero, Thread MLE operation is enabled, and there is at least one // "valid" child in the child table. - VerifyOrExit(mSupervisionInterval != 0); - VerifyOrExit(mle.GetRole() != OT_DEVICE_ROLE_DISABLED); - - for (Child *child = mle.GetChildren(&numChildren); numChildren > 0; child++, numChildren--) - { - VerifyOrExit(child->GetState() != Child::kStateValid, shouldRun = true); - } - -exit: + shouldRun = ((mSupervisionInterval != 0) && (mle.GetRole() != OT_DEVICE_ROLE_DISABLED) && + mle.GetChildTable().HasChildren(ChildTable::kInStateValid)); if (shouldRun && !mTimer.IsRunning()) { diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 66f50dd42..cd27f409f 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -85,6 +85,7 @@ endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS check_PROGRAMS = \ test-aes \ test-child \ + test-child-table \ test-heap \ test-hmac-sha256 \ test-lowpan \ @@ -147,6 +148,9 @@ test_aes_SOURCES = test_platform.cpp test_aes.cpp test_child_LDADD = $(COMMON_LDADD) test_child_SOURCES = test_platform.cpp test_child.cpp +test_child_table_LDADD = $(COMMON_LDADD) +test_child_table_SOURCES = test_platform.cpp test_child_table.cpp + test_heap_LDADD = $(COMMON_LDADD) test_heap_SOURCES = test_platform.cpp test_heap.cpp @@ -212,6 +216,7 @@ PRETTY_FILES = \ $(test_address_sanitizer_SOURCES) \ $(test_aes_SOURCES) \ $(test_child_SOURCES) \ + $(test_child_table_SOURCES) \ $(test_heap_SOURCES) \ $(test_hmac_sha256_SOURCES) \ $(test_link_quality_SOURCES) \ diff --git a/tests/unit/test_child_table.cpp b/tests/unit/test_child_table.cpp new file mode 100644 index 000000000..77b4f9c38 --- /dev/null +++ b/tests/unit/test_child_table.cpp @@ -0,0 +1,412 @@ +/* + * 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 "test_util.h" +#include "common/instance.hpp" +#include "thread/child_table.hpp" + +namespace ot { + +static ot::Instance *sInstance; + +enum +{ + kMaxChildren = OPENTHREAD_CONFIG_MAX_CHILDREN, +}; + +struct TestChild +{ + Child::State mState; + uint16_t mRloc16; + otExtAddress mExtAddress; +}; + +const ChildTable::StateFilter kAllFilters[] = { + ChildTable::kInStateValid, + ChildTable::kInStateValidOrRestoring, + ChildTable::kInStateChildIdRequest, + ChildTable::kInStateValidOrAttaching, + ChildTable::kInStateAnyExceptInvalid, +}; + +// Checks whether a `Child` matches the `TestChild` struct. +static bool ChildMatches(const Child &aChild, const TestChild &aTestChild) +{ + return (aChild.GetState() == aTestChild.mState) && (aChild.GetRloc16() == aTestChild.mRloc16) && + (aChild.GetExtAddress() == static_cast(aTestChild.mExtAddress)); +} + +// Checks whether a `Child::State` matches a `ChildTable::StateFilter`. +static bool StateMatchesFilter(Child::State aState, ChildTable::StateFilter aFilter) +{ + bool rval = false; + Child child; + + child.SetState(aState); + + switch (aFilter) + { + case ChildTable::kInStateAnyExceptInvalid: + rval = (aState != Child::kStateInvalid); + break; + + case ChildTable::kInStateValid: + rval = (aState == Child::kStateValid); + break; + + case ChildTable::kInStateValidOrRestoring: + rval = child.IsStateValidOrRestoring(); + break; + + case ChildTable::kInStateChildIdRequest: + rval = (aState == Child::kStateChildIdRequest); + break; + + case ChildTable::kInStateValidOrAttaching: + rval = child.IsStateValidOrAttaching(); + break; + + case ChildTable::kInStateAnyExceptValidOrRestoing: + rval = !child.IsStateValidOrRestoring(); + break; + } + + return rval; +} + +// Verifies that `ChildTable` contains a given list of `TestChild` entries. +void VerifyChildTableContent(ChildTable &aTable, uint8_t aChildListLength, const TestChild *aChildList) +{ + printf("Test ChildTable with %d entries", aChildListLength); + + for (uint8_t k = 0; k < sizeof(kAllFilters) / sizeof(kAllFilters[0]); k++) + { + ChildTable::StateFilter filter = kAllFilters[k]; + + // Verify that we can find all children from given list by rloc or extended address. + + for (uint8_t listIndex = 0; listIndex < aChildListLength; listIndex++) + { + Child * child; + Mac::Address address; + + if (!StateMatchesFilter(aChildList[listIndex].mState, filter)) + { + continue; + } + + child = aTable.FindChild(aChildList[listIndex].mRloc16, filter); + VerifyOrQuit(child != NULL, "FindChild(rloc) failed"); + VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(rloc) returned incorrect child"); + + child = aTable.FindChild(static_cast(aChildList[listIndex].mExtAddress), filter); + VerifyOrQuit(child != NULL, "FindChild(ExtAddress) failed"); + VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(ExtAddress) returned incorrect child"); + + address.SetShort(aChildList[listIndex].mRloc16); + child = aTable.FindChild(address, filter); + VerifyOrQuit(child != NULL, "FindChild(address) failed"); + VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child"); + + address.SetExtended(static_cast(aChildList[listIndex].mExtAddress)); + child = aTable.FindChild(address, filter); + VerifyOrQuit(child != NULL, "FindChild(address) failed"); + VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child"); + } + + // Verify `ChildTable::Iterator` behavior when starting from different child entries. + + for (uint8_t listIndex = 0; listIndex <= aChildListLength; listIndex++) + { + Child *startingChild = NULL; + + if (listIndex < aChildListLength) + { + startingChild = aTable.FindChild(aChildList[listIndex].mRloc16, ChildTable::kInStateAnyExceptInvalid); + VerifyOrQuit(startingChild != NULL, "FindChild() failed"); + } + + // Test an iterator starting from `startingChild`. + + { + ChildTable::Iterator iter(*sInstance, filter, startingChild); + bool childObserved[kMaxChildren]; + uint8_t numChildren = 0; + + memset(childObserved, 0, sizeof(childObserved)); + + // Check if the first entry matches the `startingChild` + + if ((startingChild != NULL) && StateMatchesFilter(startingChild->GetState(), filter)) + { + VerifyOrQuit(!iter.IsDone(), "iterator IsDone() failed"); + VerifyOrQuit(iter.GetChild() != NULL, "iterator GetChild() failed"); + VerifyOrQuit(iter.GetChild() == startingChild, + "Iterator failed to start from the given child entry"); + + iter.Advance(); + iter.Reset(); + VerifyOrQuit(iter.GetChild() == startingChild, "iterator Reset() failed"); + } + + // Use the iterator and verify that each returned `Child` entry is in the expected list. + + for (; !iter.IsDone(); iter.Advance()) + { + Child * child = iter.GetChild(); + bool didFind = false; + uint8_t childIndex; + + VerifyOrQuit(child != NULL, "iter.GetChild() failed"); + + childIndex = aTable.GetChildIndex(*child); + VerifyOrQuit(childIndex < aTable.GetMaxChildrenAllowed(), "Child Index is out of bound"); + VerifyOrQuit(aTable.GetChildAtIndex(childIndex) == child, "GetChildAtIndex() failed"); + + for (uint8_t index = 0; index < aChildListLength; index++) + { + if (ChildMatches(*iter.GetChild(), aChildList[index])) + { + childObserved[index] = true; + numChildren++; + didFind = true; + break; + } + } + + VerifyOrQuit(didFind, "ChildTable::Iterator returned an entry not in the expected list"); + } + + // Verify that when iterator is done, it points to `NULL`. + + VerifyOrQuit(iter.GetChild() == NULL, "iterator GetChild() failed"); + + iter.Advance(); + VerifyOrQuit(iter.IsDone(), "iterator Advance() (after iterator is done) failed"); + VerifyOrQuit(iter.GetChild() == NULL, "iterator GetChild() failed"); + + // Verify that the number of children matches the number of entries we get from iterator. + + VerifyOrQuit(aTable.GetNumChildren(filter) == numChildren, "GetNumChildren() failed"); + VerifyOrQuit(aTable.HasChildren(filter) == (numChildren != 0), "HasChildren() failed"); + + // Verify that there is no missing or extra entry between the expected list + // and what was observed/returned by the iterator. + + for (uint8_t index = 0; index < aChildListLength; index++) + { + if (StateMatchesFilter(aChildList[index].mState, filter)) + { + VerifyOrQuit(childObserved[index], "iterator failed to return an expected entry"); + } + else + { + VerifyOrQuit(!childObserved[index], "iterator returned an extra unexpected entry"); + } + } + } + } + } + + printf(" -- PASS\n"); +} + +void TestChildTable(void) +{ + const TestChild testChildList[] = { + { + Child::kStateValid, + 0x8001, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}}, + }, + { + Child::kStateParentRequest, + 0x8002, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}}, + }, + { + Child::kStateValid, + 0x8003, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}}, + }, + { + Child::kStateValid, + 0x8004, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x19}}, + }, + { + Child::kStateRestored, + 0x8005, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x20}}, + }, + { + Child::kStateValid, + 0x8006, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x21}}, + }, + { + Child::kStateChildIdRequest, + 0x8007, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x22}}, + }, + { + Child::kStateChildUpdateRequest, + 0x8008, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x23}}, + }, + { + Child::kStateParentResponse, + 0x8009, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x24}}, + }, + { + Child::kStateRestored, + 0x800a, + {{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x25}}, + }, + }; + + const uint8_t testListLength = sizeof(testChildList) / sizeof(testChildList[0]); + + uint8_t testNumAllowedChildren = 2; + + ChildTable *table; + otError error; + + sInstance = testInitInstance(); + VerifyOrQuit(sInstance != NULL, "Null instance"); + + table = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Checking initial state after child table is constructed"); + + VerifyOrQuit(table->GetMaxChildrenAllowed() == table->GetMaxChildren(), + "GetMaxChildrenAllowed() initial value is incorrect "); + + for (uint8_t i = 0; i < sizeof(kAllFilters) / sizeof(kAllFilters[0]); i++) + { + ChildTable::StateFilter filter = kAllFilters[i]; + + VerifyOrQuit(table->HasChildren(filter) == false, "HasChildren() failed after init"); + VerifyOrQuit(table->GetNumChildren(filter) == 0, "GetNumChildren() failed after init"); + } + + printf(" -- PASS\n"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + VerifyChildTableContent(*table, 0, testChildList); + + VerifyOrQuit(table->GetMaxChildrenAllowed() >= testListLength, + "Default child table size is too small for the unit test"); + + // Add the child entries from test list one by one and verify the table content + for (uint8_t i = 0; i < testListLength; i++) + { + Child *child; + + child = table->GetNewChild(); + VerifyOrQuit(child != NULL, "GetNewChild() failed"); + + child->SetState(testChildList[i].mState); + child->SetRloc16(testChildList[i].mRloc16); + child->SetExtAddress((static_cast(testChildList[i].mExtAddress))); + + VerifyChildTableContent(*table, i + 1, testChildList); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Verify Child Table clear + + table->Clear(); + + VerifyChildTableContent(*table, 0, testChildList); + + // Add the child entries from test list in reverse order and verify the table content + for (uint8_t i = testListLength; i > 0; i--) + { + Child *child; + + child = table->GetNewChild(); + VerifyOrQuit(child != NULL, "GetNewChild() failed"); + + child->SetState(testChildList[i - 1].mState); + child->SetRloc16(testChildList[i - 1].mRloc16); + child->SetExtAddress((static_cast(testChildList[i - 1].mExtAddress))); + + VerifyChildTableContent(*table, testListLength - i + 1, &testChildList[i - 1]); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + printf("Test Get/SetMaxChildrenAllowed"); + + error = table->SetMaxChildrenAllowed(kMaxChildren - 1); + VerifyOrQuit(error == OT_ERROR_INVALID_STATE, "SetMaxChildrenAllowed() should fail when table is not empty"); + + table->Clear(); + error = table->SetMaxChildrenAllowed(kMaxChildren + 1); + VerifyOrQuit(error == OT_ERROR_INVALID_ARGS, "SetMaxChildrenAllowed() did not fail with an invalid arg"); + + error = table->SetMaxChildrenAllowed(0); + VerifyOrQuit(error == OT_ERROR_INVALID_ARGS, "SetMaxChildrenAllowed() did not fail with an invalid arg"); + + error = table->SetMaxChildrenAllowed(testNumAllowedChildren); + VerifyOrQuit(error == OT_ERROR_NONE, "SetMaxChildrenAllowed() failed"); + VerifyOrQuit(table->GetMaxChildrenAllowed() == testNumAllowedChildren, "GetMaxChildrenAllowed() failed"); + + for (uint8_t num = 0; num < testNumAllowedChildren; num++) + { + Child *child = table->GetNewChild(); + + VerifyOrQuit(child != NULL, "GetNewChild() failed"); + child->SetState(Child::kStateValid); + } + + VerifyOrQuit(table->GetNewChild() == NULL, "GetNewChild() did not fail when table was full"); + + printf(" -- PASS\n"); + + testFreeInstance(sInstance); +} + +} // namespace ot + +#ifdef ENABLE_TEST_MAIN +int main(void) +{ + ot::TestChildTable(); + printf("\nAll tests passed.\n"); + return 0; +} +#endif