diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index 3a59fe555..c27a9483c 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -578,6 +578,37 @@ int8_t otThreadGetParentPriority(otInstance *aInstance); */ otError otThreadSetParentPriority(otInstance *aInstance, int8_t aParentPriority); +/** + * This function gets the maximum number of IP addresses that each MTD child may register with this device as parent. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The maximum number of IP addresses that each MTD child may register with this device as parent. + * + * @sa otThreadSetMaxChildIpAddresses + * + */ +uint8_t otThreadGetMaxChildIpAddresses(otInstance *aInstance); + +/** + * This function sets/restores the maximum number of IP addresses that each MTD child may register with this + * device as parent. + * + * @note This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is only used by Thread Test Harness + * to limit the address registrations of the reference parent in order to test the MTD DUT reaction. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMaxIpAddresses The maximum number of IP addresses that each MTD child may register with this + * device as parent. 0 to clear the setting and restore the default. + * + * @retval OT_ERROR_NONE Successfully set/cleared the number. + * @retval OT_ERROR_INVALID_ARGS If exceeds the allowed maximum number. + * + * @sa otThreadGetMaxChildIpAddresses + * + */ +otError otThreadSetMaxChildIpAddresses(otInstance *aInstance, uint8_t aMaxIpAddresses); + /** * This enumeration defines the constants used in `otNeighborTableCallback` to indicate whether a child or router * neighbor is being added or removed. diff --git a/src/cli/README.md b/src/cli/README.md index b57e74f40..5dccb8482 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -333,6 +333,28 @@ Get the list of IP addresses stored for MTD children. Done ``` +### childip max + +Get the maximum number of IP addresses that each MTD child may register with this device as parent. + +```bash +> childip max +4 +Done +``` + +### childip max \ + +Set the maximum number of IP addresses that each MTD child may register with this device as parent. +0 to clear the setting and restore the default. + +`OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required. + +```bash +> childip max 2 +Done +``` + ### childmax Get the Thread maximum number of allowed children. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index c1115602c..e3746a33c 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -864,37 +864,62 @@ exit: void Interpreter::ProcessChildIp(uint8_t aArgsLength, char *aArgs[]) { - otError error = OT_ERROR_NONE; - uint16_t maxChildren; + OT_UNUSED_VARIABLE(aArgs); + otError error = OT_ERROR_NONE; - VerifyOrExit(aArgsLength == 0, error = OT_ERROR_INVALID_ARGS); - - maxChildren = otThreadGetMaxAllowedChildren(mInstance); - - for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++) + if (aArgsLength == 0) { - otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; - otIp6Address ip6Address; - otChildInfo childInfo; + uint16_t maxChildren = otThreadGetMaxAllowedChildren(mInstance); - if ((otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) != OT_ERROR_NONE) || - childInfo.mIsStateRestoring) + for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++) { - continue; - } + otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; + otIp6Address ip6Address; + otChildInfo childInfo; - iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; + if ((otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) != OT_ERROR_NONE) || + childInfo.mIsStateRestoring) + { + continue; + } - while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE) - { - mServer->OutputFormat("%04x: ", childInfo.mRloc16); - OutputIp6Address(ip6Address); - mServer->OutputFormat("\r\n"); + iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; + + while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE) + { + mServer->OutputFormat("%04x: ", childInfo.mRloc16); + OutputIp6Address(ip6Address); + mServer->OutputFormat("\r\n"); + } } } + else if (strcmp(aArgs[0], "max") == 0) + { + if (aArgsLength == 1) + { + mServer->OutputFormat("%d\r\n", otThreadGetMaxChildIpAddresses(mInstance)); + } +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + else if (aArgsLength == 2) + { + unsigned long value; + SuccessOrExit(error = ParseUnsignedLong(aArgs[1], value)); + SuccessOrExit(error = otThreadSetMaxChildIpAddresses(mInstance, static_cast(value))); + } +#endif + else + { + error = OT_ERROR_INVALID_ARGS; + } + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE exit: - OT_UNUSED_VARIABLE(aArgs); +#endif AppendResult(error); } diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index 3c4a79883..c102e84f3 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -58,6 +58,22 @@ otError otThreadSetMaxAllowedChildren(otInstance *aInstance, uint16_t aMaxChildr return instance.Get().SetMaxChildrenAllowed(aMaxChildren); } +uint8_t otThreadGetMaxChildIpAddresses(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().GetMaxChildIpAddresses(); +} + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +otError otThreadSetMaxChildIpAddresses(otInstance *aInstance, uint8_t aMaxIpAddresses) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().SetMaxChildIpAddresses(aMaxIpAddresses); +} +#endif + bool otThreadIsRouterEligible(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 90cfb4517..e4c5e9ce7 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -83,6 +83,9 @@ MleRouter::MleRouter(Instance &aInstance) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE , mBackboneRouterRegistrationDelay(0) #endif +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + , mMaxChildIpAddresses(0) +#endif { mDeviceMode.Set(mDeviceMode.Get() | DeviceMode::kModeFullThreadDevice | DeviceMode::kModeFullNetworkData); @@ -1984,6 +1987,34 @@ exit: } } +uint8_t MleRouter::GetMaxChildIpAddresses(void) const +{ + uint8_t num = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD; + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + if (mMaxChildIpAddresses != 0) + { + num = mMaxChildIpAddresses; + } +#endif + + return num; +} + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +otError MleRouter::SetMaxChildIpAddresses(uint8_t aMaxIpAddresses) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aMaxIpAddresses <= OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD, error = OT_ERROR_INVALID_ARGS); + + mMaxChildIpAddresses = aMaxIpAddresses; + +exit: + return error; +} +#endif + otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, Child &aChild) { otError error = OT_ERROR_NONE; @@ -2034,10 +2065,20 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse address = *entry.GetIp6Address(); } - // We try to accept/add as many IPv6 addresses as possible. - // "Child ID/Update Response" will indicate the accepted - // addresses. - error = aChild.AddIp6Address(address); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + if (mMaxChildIpAddresses > 0 && storedCount >= mMaxChildIpAddresses) + { + // Skip remaining address registration entries but keep logging skipped addresses. + error = OT_ERROR_NO_BUFS; + } + else +#endif + { + // We try to accept/add as many IPv6 addresses as possible. + // "Child ID/Update Response" will indicate the accepted + // addresses. + error = aChild.AddIp6Address(address); + } if (error == OT_ERROR_NONE) { diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 0fa17b6ad..a84ec5958 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -677,6 +677,29 @@ public: void SetBackboneRouterRegistrationDelay(uint8_t aDelay) { mBackboneRouterRegistrationDelay = aDelay; } #endif + /** + * This method gets the maximum number of IP addresses that each MTD child may register with this device as parent. + * + * @returns The maximum number of IP addresses that each MTD child may register with this device as parent. + * + */ + uint8_t GetMaxChildIpAddresses(void) const; + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + /** + * This method sets/restores the maximum number of IP addresses that each MTD child may register with this + * device as parent. + * + * @param[in] aMaxIpAddresses The maximum number of IP addresses that each MTD child may register with this + * device as parent. 0 to clear the setting and restore the default. + * + * @retval OT_ERROR_NONE Successfully set/cleared the number. + * @retval OT_ERROR_INVALID_ARGS If exceeds the allowed maximum number. + * + */ + otError SetMaxChildIpAddresses(uint8_t aMaxIpAddresses); +#endif + private: enum { @@ -824,6 +847,9 @@ private: #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE uint8_t mBackboneRouterRegistrationDelay; ///< Delay before registering Backbone Router service. #endif +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + uint8_t mMaxChildIpAddresses; +#endif #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE MeshCoP::SteeringDataTlv mSteeringData;