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