mirror of
https://github.com/espressif/openthread.git
synced 2026-08-17 07:59:51 +00:00
[mlr] add Commissioner API to register Multicast Listeners (#5346)
This commit is contained in:
@@ -653,6 +653,59 @@ typedef bool (*otIp6SlaacPrefixFilter)(otInstance *aInstance, const otIp6Prefix
|
||||
*/
|
||||
void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFilter);
|
||||
|
||||
/**
|
||||
* This function pointer is called with results of `otIp6RegisterMulticastListeners`.
|
||||
*
|
||||
* @param[in] aContext A pointer to the user context.
|
||||
* @param[in] aError OT_ERROR_NONE when successfully sent MLR.req and received MLR.rsp,
|
||||
* OT_ERROR_RESPONSE_TIMEOUT when failed to receive MLR.rsp,
|
||||
* OT_ERROR_PARSE when failed to parse MLR.rsp.
|
||||
* @param[in] aMlrStatus The Multicast Listener Registration status when @p aError is OT_ERROR_NONE.
|
||||
* @param[in] aFailedAddresses A pointer to the failed Ip6 addresses when @p aError is OT_ERROR_NONE.
|
||||
* @param[in] aFailedAddressNum The number of failed Ip6 addresses when @p aError is OT_ERROR_NONE.
|
||||
*
|
||||
* @sa otIp6RegisterMulticastListeners
|
||||
*
|
||||
*/
|
||||
typedef void (*otIp6RegisterMulticastListenersCallback)(void * aContext,
|
||||
otError aError,
|
||||
uint8_t aMlrStatus,
|
||||
const otIp6Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum);
|
||||
|
||||
/**
|
||||
* This function registers Multicast Listeners to Primary Backbone Router.
|
||||
*
|
||||
* Note: only available when both `OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE` and
|
||||
* `OPENTHREAD_CONFIG_COMMISSIONER_ENABLE` are enabled)
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aAddresses A Multicast Address Array to register.
|
||||
* @param[in] aAddressNum The number of Multicast Address to register (0 if @p aAddresses is NULL).
|
||||
* @param[in] aTimeout A pointer to the timeout value (in seconds) to be included in MLR.req. A timeout value of 0
|
||||
* removes the corresponding Multicast Listener. If NULL, MLR.req would have no Timeout Tlv by
|
||||
* default.
|
||||
* @param[in] aCallback A pointer to the callback function.
|
||||
* @param[in] aContext A pointer to the user context.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully sent MLR.req. The @p aCallback will be called iff this method
|
||||
* returns OT_ERROR_NONE.
|
||||
* @retval OT_ERROR_BUSY If a previous registration was ongoing.
|
||||
* @retval OT_ERROR_INVALID_ARGS If one or more arguments are invalid.
|
||||
* @retval OT_ERROR_INVALID_STATE If the device was not in a valid state to send MLR.req (e.g. Commissioner not
|
||||
* started, Primary Backbone Router not found).
|
||||
* @retval OT_ERROR_NO_BUFS If insufficient message buffers available.
|
||||
*
|
||||
* @sa otIp6RegisterMulticastListenersCallback
|
||||
*
|
||||
*/
|
||||
otError otIp6RegisterMulticastListeners(otInstance * aInstance,
|
||||
const otIp6Address * aAddresses,
|
||||
uint8_t aAddressNum,
|
||||
const uint32_t * aTimeout,
|
||||
otIp6RegisterMulticastListenersCallback aCallback,
|
||||
void * aContext);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -60,6 +60,7 @@ Done
|
||||
- [mac](#mac-retries-direct)
|
||||
- [macfilter](#macfilter)
|
||||
- [masterkey](#masterkey)
|
||||
- [mlr](#mlr-reg-ipaddr--timeout)
|
||||
- [mode](#mode)
|
||||
- [neighbor](#neighbor-list)
|
||||
- [netdata](#netdata-steeringdata-check-eui64discerner)
|
||||
@@ -1097,6 +1098,32 @@ Set the Thread Master Key value.
|
||||
Done
|
||||
```
|
||||
|
||||
### mlr reg \<ipaddr\> ... [timeout]
|
||||
|
||||
Register Multicast Listeners to Primary Backbone Router, with an optional `timeout` (in seconds).
|
||||
|
||||
Omit `timeout` to use the default MLR timeout on the Primary Backbone Router.
|
||||
|
||||
Use `timeout = 0` to deregister Multicast Listeners.
|
||||
|
||||
NOTE: Only for Thread 1.2 Commissioner FTD device.
|
||||
|
||||
```bash
|
||||
> mlr reg ff04::1
|
||||
status 0, 0 failed
|
||||
Done
|
||||
> mlr reg ff04::1 ff04::2 ff02::1
|
||||
status 2, 1 failed
|
||||
ff02:0:0:0:0:0:0:1
|
||||
Done
|
||||
> mlr reg ff04::1 ff04::2 1000
|
||||
status 0, 0 failed
|
||||
Done
|
||||
> mlr reg ff04::1 ff04::2 0
|
||||
status 0, 0 failed
|
||||
Done
|
||||
```
|
||||
|
||||
### mode
|
||||
|
||||
Get the Thread Device Mode value.
|
||||
|
||||
@@ -184,6 +184,9 @@ const struct Command Interpreter::sCommands[] = {
|
||||
{"macfilter", &Interpreter::ProcessMacFilter},
|
||||
#endif
|
||||
{"masterkey", &Interpreter::ProcessMasterKey},
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
{"mlr", &Interpreter::ProcessMlr},
|
||||
#endif
|
||||
{"mode", &Interpreter::ProcessMode},
|
||||
#if OPENTHREAD_FTD
|
||||
{"neighbor", &Interpreter::ProcessNeighbor},
|
||||
@@ -2070,6 +2073,94 @@ exit:
|
||||
AppendResult(error);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
|
||||
void Interpreter::ProcessMlr(uint8_t aArgsLength, char **aArgs)
|
||||
{
|
||||
if (aArgsLength == 0)
|
||||
{
|
||||
AppendResult(OT_ERROR_INVALID_COMMAND);
|
||||
}
|
||||
else if (!strcmp(aArgs[0], "reg"))
|
||||
{
|
||||
ProcessMlrReg(aArgsLength - 1, aArgs + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendResult(OT_ERROR_INVALID_COMMAND);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::ProcessMlrReg(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
otIp6Address addresses[kIPv6AddressesNumMax];
|
||||
uint32_t timeout;
|
||||
uint8_t i;
|
||||
|
||||
VerifyOrExit(aArgsLength >= 1, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aArgsLength <= kIPv6AddressesNumMax + 1, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
for (i = 0; i < aArgsLength && i < kIPv6AddressesNumMax; i++)
|
||||
{
|
||||
if (otIp6AddressFromString(aArgs[i], &addresses[i]) != OT_ERROR_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(i > 0 && (i == aArgsLength || i == aArgsLength - 1), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
if (i == aArgsLength - 1)
|
||||
{
|
||||
// Parse the last argument as a timeout in seconds
|
||||
unsigned long value;
|
||||
|
||||
SuccessOrExit(error = ParseUnsignedLong(aArgs[i], value));
|
||||
|
||||
timeout = static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
error = otIp6RegisterMulticastListeners(mInstance, addresses, i, i == aArgsLength - 1 ? &timeout : nullptr,
|
||||
Interpreter::HandleMlrRegResult, this);
|
||||
|
||||
exit:
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
AppendResult(error);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::HandleMlrRegResult(void * aContext,
|
||||
otError aError,
|
||||
uint8_t aMlrStatus,
|
||||
const otIp6Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum)
|
||||
{
|
||||
static_cast<Interpreter *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum);
|
||||
}
|
||||
|
||||
void Interpreter::HandleMlrRegResult(otError aError,
|
||||
uint8_t aMlrStatus,
|
||||
const otIp6Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum)
|
||||
{
|
||||
if (aError == OT_ERROR_NONE)
|
||||
{
|
||||
OutputFormat("status %d, %d failed\r\n", aMlrStatus, aFailedAddressNum);
|
||||
|
||||
for (uint8_t i = 0; i < aFailedAddressNum; i++)
|
||||
{
|
||||
OutputIp6Address(aFailedAddresses[i]);
|
||||
OutputFormat("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
AppendResult(aError);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
|
||||
void Interpreter::ProcessMode(uint8_t aArgsLength, char *aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
@@ -354,6 +354,23 @@ private:
|
||||
void ProcessLeaderWeight(uint8_t aArgsLength, char *aArgs[]);
|
||||
#endif
|
||||
void ProcessMasterKey(uint8_t aArgsLength, char *aArgs[]);
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||
void ProcessMlr(uint8_t aArgsLength, char *aArgs[]);
|
||||
|
||||
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
void ProcessMlrReg(uint8_t aArgsLength, char *aArgs[]);
|
||||
|
||||
static void HandleMlrRegResult(void * aContext,
|
||||
otError aError,
|
||||
uint8_t aMlrStatus,
|
||||
const otIp6Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum);
|
||||
void HandleMlrRegResult(otError aError,
|
||||
uint8_t aMlrStatus,
|
||||
const otIp6Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum);
|
||||
#endif
|
||||
#endif
|
||||
void ProcessMode(uint8_t aArgsLength, char *aArgs[]);
|
||||
#if OPENTHREAD_FTD
|
||||
void ProcessNeighbor(uint8_t aArgsLength, char *aArgs[]);
|
||||
|
||||
@@ -286,4 +286,19 @@ void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFi
|
||||
instance.Get<Utils::Slaac>().SetFilter(aFilter);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
otError otIp6RegisterMulticastListeners(otInstance * aInstance,
|
||||
const otIp6Address * aAddresses,
|
||||
uint8_t aAddressNum,
|
||||
const uint32_t * aTimeout,
|
||||
otIp6RegisterMulticastListenersCallback aCallback,
|
||||
void * aContext)
|
||||
{
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
return instance.Get<MlrManager>().RegisterMulticastListeners(aAddresses, aAddressNum, aTimeout, aCallback,
|
||||
aContext);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "bbr_manager.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
@@ -99,13 +101,19 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage,
|
||||
otError error = OT_ERROR_NONE;
|
||||
bool isPrimary = Get<BackboneRouter::Local>().IsPrimary();
|
||||
ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess;
|
||||
uint16_t addressesOffset, addressesLength;
|
||||
Ip6::Address address;
|
||||
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
|
||||
uint8_t failedAddressNum = 0;
|
||||
TimeMilli expireTime;
|
||||
BackboneRouterConfig config;
|
||||
|
||||
uint16_t addressesOffset, addressesLength;
|
||||
Ip6::Address address;
|
||||
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
|
||||
uint8_t failedAddressNum = 0;
|
||||
TimeMilli expireTime;
|
||||
|
||||
uint32_t timeout;
|
||||
uint16_t commissionerSessionId;
|
||||
bool hasCommissionerSessionIdTlv = false;
|
||||
bool processTimeoutTlv = false;
|
||||
|
||||
VerifyOrExit(aMessage.IsConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST, error = OT_ERROR_PARSE);
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
@@ -119,8 +127,23 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage,
|
||||
|
||||
VerifyOrExit(isPrimary, status = ThreadStatusTlv::kMlrBbrNotPrimary);
|
||||
|
||||
// TODO: (MLR) handle Commissioner Session TLV
|
||||
// TODO: (MLR) handle Timeout TLV
|
||||
// TODO: (MLR) send configured MLR response for Reference Device
|
||||
|
||||
if (ThreadTlv::FindUint16Tlv(aMessage, ThreadTlv::kCommissionerSessionId, commissionerSessionId) == OT_ERROR_NONE)
|
||||
{
|
||||
const MeshCoP::CommissionerSessionIdTlv *commissionerSessionIdTlv =
|
||||
static_cast<const MeshCoP::CommissionerSessionIdTlv *>(
|
||||
Get<NetworkData::Leader>().GetCommissioningDataSubTlv(MeshCoP::Tlv::kCommissionerSessionId));
|
||||
|
||||
VerifyOrExit(commissionerSessionIdTlv != nullptr &&
|
||||
commissionerSessionIdTlv->GetCommissionerSessionId() == commissionerSessionId,
|
||||
status = ThreadStatusTlv::kMlrGeneralFailure);
|
||||
|
||||
hasCommissionerSessionIdTlv = true;
|
||||
}
|
||||
|
||||
processTimeoutTlv = hasCommissionerSessionIdTlv &&
|
||||
(ThreadTlv::FindUint32Tlv(aMessage, ThreadTlv::kTimeout, timeout) == OT_ERROR_NONE);
|
||||
|
||||
VerifyOrExit(ThreadTlv::FindTlvValueOffset(aMessage, IPv6AddressesTlv::kIPv6Addresses, addressesOffset,
|
||||
addressesLength) == OT_ERROR_NONE,
|
||||
@@ -129,39 +152,69 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage,
|
||||
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIPv6AddressesNumMax,
|
||||
status = ThreadStatusTlv::kMlrGeneralFailure);
|
||||
|
||||
IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
|
||||
expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(config.mMlrTimeout);
|
||||
if (!processTimeoutTlv)
|
||||
{
|
||||
IgnoreError(Get<BackboneRouter::Leader>().GetConfig(config));
|
||||
|
||||
timeout = config.mMlrTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyOrExit(timeout < std::numeric_limits<uint32_t>::max(), status = ThreadStatusTlv::kMlrNoPersistent);
|
||||
|
||||
if (timeout != 0)
|
||||
{
|
||||
uint32_t origTimeout = timeout;
|
||||
|
||||
timeout = OT_MIN(timeout, static_cast<uint32_t>(Mle::kMlrTimeoutMax));
|
||||
timeout = OT_MAX(timeout, static_cast<uint32_t>(Mle::kMlrTimeoutMin));
|
||||
|
||||
if (timeout != origTimeout)
|
||||
{
|
||||
otLogNoteBbr("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(timeout);
|
||||
|
||||
for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
|
||||
{
|
||||
bool failed = true;
|
||||
|
||||
IgnoreReturnValue(aMessage.Read(addressesOffset + offset, sizeof(Ip6::Address), &address));
|
||||
|
||||
switch (mMulticastListenersTable.Add(address, expireTime))
|
||||
if (timeout == 0)
|
||||
{
|
||||
case OT_ERROR_NONE:
|
||||
failed = false;
|
||||
break;
|
||||
case OT_ERROR_INVALID_ARGS:
|
||||
if (status == ThreadStatusTlv::kMlrSuccess)
|
||||
{
|
||||
status = ThreadStatusTlv::kMlrInvalid;
|
||||
}
|
||||
break;
|
||||
case OT_ERROR_NO_BUFS:
|
||||
if (status == ThreadStatusTlv::kMlrSuccess)
|
||||
{
|
||||
status = ThreadStatusTlv::kMlrNoResources;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OT_ASSERT(false);
|
||||
mMulticastListenersTable.Remove(address);
|
||||
}
|
||||
|
||||
if (failed)
|
||||
else
|
||||
{
|
||||
failedAddresses[failedAddressNum++] = address;
|
||||
bool failed = true;
|
||||
|
||||
switch (mMulticastListenersTable.Add(address, expireTime))
|
||||
{
|
||||
case OT_ERROR_NONE:
|
||||
failed = false;
|
||||
break;
|
||||
case OT_ERROR_INVALID_ARGS:
|
||||
if (status == ThreadStatusTlv::kMlrSuccess)
|
||||
{
|
||||
status = ThreadStatusTlv::kMlrInvalid;
|
||||
}
|
||||
break;
|
||||
case OT_ERROR_NO_BUFS:
|
||||
if (status == ThreadStatusTlv::kMlrSuccess)
|
||||
{
|
||||
status = ThreadStatusTlv::kMlrNoResources;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OT_ASSERT(false);
|
||||
}
|
||||
|
||||
if (failed)
|
||||
{
|
||||
failedAddresses[failedAddressNum++] = address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,18 @@
|
||||
#define OT_DEFINE_ALIGNED_VAR(name, size, align_type) \
|
||||
align_type name[(((size) + (sizeof(align_type) - 1)) / sizeof(align_type))]
|
||||
|
||||
/**
|
||||
* This macro returns the smaller of @p a and @p b.
|
||||
*
|
||||
*/
|
||||
#define OT_MIN(a, b) ((b) < (a) ? (b) : (a))
|
||||
|
||||
/**
|
||||
* This macro returns the greater of @p a and @p b.
|
||||
*
|
||||
*/
|
||||
#define OT_MAX(a, b) ((a) < (b) ? (b) : (a))
|
||||
|
||||
/**
|
||||
* This macro checks for the specified status, which is expected to commonly be successful, and branches to the local
|
||||
* label 'exit' if the status is unsuccessful.
|
||||
|
||||
@@ -189,7 +189,12 @@
|
||||
* This is compulsory for 1.2 FTD.
|
||||
*
|
||||
*/
|
||||
#ifdef OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
|
||||
#error \
|
||||
"Don't define `OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE` manually. It's an alias of `((OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_FTD)`."
|
||||
#else
|
||||
#define OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE \
|
||||
((OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_FTD)
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_TMF_H_
|
||||
|
||||
+192
-60
@@ -48,9 +48,16 @@ namespace ot {
|
||||
|
||||
MlrManager::MlrManager(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
, mRegisterMulticastListenersCallback(nullptr)
|
||||
, mRegisterMulticastListenersContext(nullptr)
|
||||
#endif
|
||||
, mReregistrationDelay(0)
|
||||
, mSendDelay(0)
|
||||
, mMlrPending(false)
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
, mRegisterMulticastListenersPending(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -218,13 +225,10 @@ void MlrManager::UpdateTimeTickerRegistration(void)
|
||||
|
||||
void MlrManager::SendMulticastListenerRegistration(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Mle::MleRouter & mle = Get<Mle::MleRouter>();
|
||||
Coap::Message * message = nullptr;
|
||||
Ip6::MessageInfo messageInfo;
|
||||
IPv6AddressesTlv addressesTlv;
|
||||
Ip6::Address addresses[kIPv6AddressesNumMax];
|
||||
uint8_t addressesNum = 0;
|
||||
otError error;
|
||||
Mle::MleRouter &mle = Get<Mle::MleRouter>();
|
||||
Ip6::Address addresses[kIPv6AddressesNumMax];
|
||||
uint8_t addressesNum = 0;
|
||||
|
||||
VerifyOrExit(!mMlrPending, error = OT_ERROR_BUSY);
|
||||
VerifyOrExit(mle.IsAttached(), error = OT_ERROR_INVALID_STATE);
|
||||
@@ -280,6 +284,113 @@ void MlrManager::SendMulticastListenerRegistration(void)
|
||||
#endif
|
||||
|
||||
VerifyOrExit(addressesNum > 0, error = OT_ERROR_NOT_FOUND);
|
||||
SuccessOrExit(
|
||||
error = SendMulticastListenerRegistrationMessage(
|
||||
addresses, addressesNum, nullptr, &MlrManager::HandleMulticastListenerRegistrationResponse, this));
|
||||
|
||||
mMlrPending = true;
|
||||
|
||||
// TODO: not enable fast polls for SSED
|
||||
if (!Get<Mle::Mle>().IsRxOnWhenIdle())
|
||||
{
|
||||
Get<DataPollSender>().SendFastPolls(DataPollSender::kDefaultFastPolls);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
|
||||
|
||||
if (error == OT_ERROR_NO_BUFS)
|
||||
{
|
||||
ScheduleSend(1);
|
||||
}
|
||||
}
|
||||
|
||||
LogMulticastAddresses();
|
||||
CheckInvariants();
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
otError MlrManager::RegisterMulticastListeners(const otIp6Address * aAddresses,
|
||||
uint8_t aAddressNum,
|
||||
const uint32_t * aTimeout,
|
||||
otIp6RegisterMulticastListenersCallback aCallback,
|
||||
void * aContext)
|
||||
{
|
||||
otError error;
|
||||
|
||||
VerifyOrExit(aAddresses != nullptr, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aAddressNum > 0 && aAddressNum <= kIPv6AddressesNumMax, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(aContext == nullptr || aCallback != nullptr, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
// Only allow one outstanding registration if callback is specified.
|
||||
VerifyOrExit(!mRegisterMulticastListenersPending, error = OT_ERROR_BUSY);
|
||||
|
||||
SuccessOrExit(error = SendMulticastListenerRegistrationMessage(
|
||||
aAddresses, aAddressNum, aTimeout, &MlrManager::HandleRegisterMulticastListenersResponse, this));
|
||||
|
||||
mRegisterMulticastListenersPending = true;
|
||||
mRegisterMulticastListenersCallback = aCallback;
|
||||
mRegisterMulticastListenersContext = aContext;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void MlrManager::HandleRegisterMulticastListenersResponse(void * aContext,
|
||||
otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult)
|
||||
{
|
||||
static_cast<MlrManager *>(aContext)->HandleRegisterMulticastListenersResponse(
|
||||
static_cast<Coap::Message *>(aMessage), static_cast<const Ip6::MessageInfo *>(aMessageInfo), aResult);
|
||||
}
|
||||
|
||||
void MlrManager::HandleRegisterMulticastListenersResponse(otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aMessageInfo);
|
||||
|
||||
uint8_t status;
|
||||
otError error;
|
||||
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
|
||||
uint8_t failedAddressNum = 0;
|
||||
otIp6RegisterMulticastListenersCallback callback = mRegisterMulticastListenersCallback;
|
||||
void * context = mRegisterMulticastListenersContext;
|
||||
|
||||
mRegisterMulticastListenersPending = false;
|
||||
mRegisterMulticastListenersCallback = nullptr;
|
||||
mRegisterMulticastListenersContext = nullptr;
|
||||
|
||||
error = ParseMulticastListenerRegistrationResponse(aResult, static_cast<Coap::Message *>(aMessage), status,
|
||||
failedAddresses, failedAddressNum);
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
callback(context, error, status, failedAddresses, failedAddressNum);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
|
||||
otError MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses,
|
||||
uint8_t aAddressNum,
|
||||
const uint32_t * aTimeout,
|
||||
Coap::ResponseHandler aResponseHandler,
|
||||
void * aResponseContext)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aTimeout);
|
||||
|
||||
otError error = OT_ERROR_NONE;
|
||||
Mle::MleRouter & mle = Get<Mle::MleRouter>();
|
||||
Coap::Message * message = nullptr;
|
||||
Ip6::MessageInfo messageInfo;
|
||||
IPv6AddressesTlv addressesTlv;
|
||||
|
||||
VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
VerifyOrExit((message = Get<Tmf::TmfAgent>().NewMessage()) != nullptr, error = OT_ERROR_NO_BUFS);
|
||||
|
||||
@@ -289,9 +400,24 @@ void MlrManager::SendMulticastListenerRegistration(void)
|
||||
SuccessOrExit(message->SetPayloadMarker());
|
||||
|
||||
addressesTlv.Init();
|
||||
addressesTlv.SetLength(sizeof(Ip6::Address) * addressesNum);
|
||||
addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
|
||||
SuccessOrExit(error = message->Append(&addressesTlv, sizeof(addressesTlv)));
|
||||
SuccessOrExit(error = message->Append(&addresses, sizeof(Ip6::Address) * addressesNum));
|
||||
SuccessOrExit(error = message->Append(aAddresses, sizeof(Ip6::Address) * aAddressNum));
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
if (Get<MeshCoP::Commissioner>().IsActive())
|
||||
{
|
||||
SuccessOrExit(error = ThreadTlv::AppendUint16Tlv(*message, ThreadTlv::kCommissionerSessionId,
|
||||
Get<MeshCoP::Commissioner>().GetSessionId()));
|
||||
}
|
||||
|
||||
if (aTimeout != nullptr)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::AppendUint32Tlv(*message, ThreadTlv::kTimeout, *aTimeout));
|
||||
}
|
||||
#else
|
||||
OT_ASSERT(aTimeout == nullptr);
|
||||
#endif
|
||||
|
||||
if (!mle.IsFullThreadDevice() && mle.GetParent().IsThreadVersion1p1())
|
||||
{
|
||||
@@ -309,36 +435,26 @@ void MlrManager::SendMulticastListenerRegistration(void)
|
||||
messageInfo.SetPeerPort(Tmf::kUdpPort);
|
||||
messageInfo.SetSockAddr(mle.GetMeshLocal16());
|
||||
|
||||
SuccessOrExit(error = Get<Tmf::TmfAgent>().SendMessage(
|
||||
*message, messageInfo, &MlrManager::HandleMulticastListenerRegistrationResponse, this));
|
||||
|
||||
mMlrPending = true;
|
||||
|
||||
// TODO: not enable fast polls for SSED
|
||||
if (!Get<Mle::Mle>().IsRxOnWhenIdle())
|
||||
{
|
||||
Get<DataPollSender>().SendFastPolls(DataPollSender::kDefaultFastPolls);
|
||||
}
|
||||
error = Get<Tmf::TmfAgent>().SendMessage(*message, messageInfo, aResponseHandler, aResponseContext);
|
||||
|
||||
exit:
|
||||
if (error != OT_ERROR_NONE)
|
||||
otLogInfoMlr("Send MLR.req: %s, addressNum=%d", otThreadErrorToString(error), aAddressNum);
|
||||
|
||||
if (error != OT_ERROR_NONE && message != nullptr)
|
||||
{
|
||||
if (message != nullptr)
|
||||
{
|
||||
message->Free();
|
||||
}
|
||||
|
||||
SetMulticastAddressMlrState(kMlrStateRegistering, kMlrStateToRegister);
|
||||
|
||||
if (error == OT_ERROR_NO_BUFS)
|
||||
{
|
||||
ScheduleSend(1);
|
||||
}
|
||||
message->Free();
|
||||
}
|
||||
|
||||
otLogInfoMlr("Send MLR.req: %s", otThreadErrorToString(error));
|
||||
LogMulticastAddresses();
|
||||
CheckInvariants();
|
||||
return error;
|
||||
}
|
||||
|
||||
void MlrManager::HandleMulticastListenerRegistrationResponse(void * aContext,
|
||||
otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult)
|
||||
{
|
||||
static_cast<MlrManager *>(aContext)->HandleMulticastListenerRegistrationResponse(
|
||||
static_cast<Coap::Message *>(aMessage), static_cast<const Ip6::MessageInfo *>(aMessageInfo), aResult);
|
||||
}
|
||||
|
||||
void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
|
||||
@@ -347,35 +463,12 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message *
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aMessageInfo);
|
||||
|
||||
uint8_t status = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint16_t addressesOffset, addressesLength;
|
||||
uint8_t status;
|
||||
otError error;
|
||||
Ip6::Address failedAddresses[kIPv6AddressesNumMax];
|
||||
uint8_t failedAddressNum = 0;
|
||||
|
||||
VerifyOrExit(aResult == OT_ERROR_NONE && aMessage != nullptr, error = OT_ERROR_PARSE);
|
||||
VerifyOrExit(aMessage->GetCode() == OT_COAP_CODE_CHANGED, error = OT_ERROR_PARSE);
|
||||
|
||||
SuccessOrExit(error = Tlv::FindUint8Tlv(*aMessage, ThreadTlv::kStatus, status));
|
||||
|
||||
if (ThreadTlv::FindTlvValueOffset(*aMessage, IPv6AddressesTlv::kIPv6Addresses, addressesOffset, addressesLength) ==
|
||||
OT_ERROR_NONE)
|
||||
{
|
||||
VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = OT_ERROR_PARSE);
|
||||
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIPv6AddressesNumMax, error = OT_ERROR_PARSE);
|
||||
|
||||
for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
|
||||
{
|
||||
IgnoreReturnValue(
|
||||
aMessage->Read(addressesOffset + offset, sizeof(Ip6::Address), &failedAddresses[failedAddressNum]));
|
||||
failedAddressNum++;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(failedAddressNum == 0 || status != ThreadStatusTlv::MlrStatus::kMlrSuccess, error = OT_ERROR_PARSE);
|
||||
|
||||
exit:
|
||||
LogMlrResponse(aResult, error, status, failedAddresses, failedAddressNum);
|
||||
error = ParseMulticastListenerRegistrationResponse(aResult, aMessage, status, failedAddresses, failedAddressNum);
|
||||
|
||||
FinishMulticastListenerRegistration(error == OT_ERROR_NONE && status == ThreadStatusTlv::MlrStatus::kMlrSuccess,
|
||||
failedAddresses, failedAddressNum);
|
||||
@@ -403,6 +496,43 @@ exit:
|
||||
}
|
||||
}
|
||||
|
||||
otError MlrManager::ParseMulticastListenerRegistrationResponse(otError aResult,
|
||||
Coap::Message *aMessage,
|
||||
uint8_t & aStatus,
|
||||
Ip6::Address * aFailedAddresses,
|
||||
uint8_t & aFailedAddressNum)
|
||||
{
|
||||
otError error;
|
||||
uint16_t addressesOffset, addressesLength;
|
||||
|
||||
aStatus = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure;
|
||||
|
||||
VerifyOrExit(aResult == OT_ERROR_NONE && aMessage != nullptr, error = OT_ERROR_PARSE);
|
||||
VerifyOrExit(aMessage->GetCode() == OT_COAP_CODE_CHANGED, error = OT_ERROR_PARSE);
|
||||
|
||||
SuccessOrExit(error = Tlv::FindUint8Tlv(*aMessage, ThreadTlv::kStatus, aStatus));
|
||||
|
||||
if (ThreadTlv::FindTlvValueOffset(*aMessage, IPv6AddressesTlv::kIPv6Addresses, addressesOffset, addressesLength) ==
|
||||
OT_ERROR_NONE)
|
||||
{
|
||||
VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, error = OT_ERROR_PARSE);
|
||||
VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= kIPv6AddressesNumMax, error = OT_ERROR_PARSE);
|
||||
|
||||
for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address))
|
||||
{
|
||||
IgnoreReturnValue(
|
||||
aMessage->Read(addressesOffset + offset, sizeof(Ip6::Address), &aFailedAddresses[aFailedAddressNum]));
|
||||
aFailedAddressNum++;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(aFailedAddressNum == 0 || aStatus != ThreadStatusTlv::MlrStatus::kMlrSuccess, error = OT_ERROR_PARSE);
|
||||
|
||||
exit:
|
||||
LogMlrResponse(aResult, error, aStatus, aFailedAddresses, aFailedAddressNum);
|
||||
return aResult != OT_ERROR_NONE ? aResult : error;
|
||||
}
|
||||
|
||||
void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||
@@ -485,6 +615,8 @@ void MlrManager::HandleTimeTick(void)
|
||||
|
||||
void MlrManager::Reregister(void)
|
||||
{
|
||||
otLogInfoMlr("MLR Reregister!");
|
||||
|
||||
SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
|
||||
CheckInvariants();
|
||||
|
||||
|
||||
@@ -107,23 +107,68 @@ public:
|
||||
uint16_t aOldMlrRegisteredAddressNum);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
/**
|
||||
* This method registers Multicast Listeners to Primary Backbone Router.
|
||||
*
|
||||
* Note: only available when both `OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE` and
|
||||
* `OPENTHREAD_CONFIG_COMMISSIONER_ENABLE` are enabled)
|
||||
*
|
||||
* @param aAddresses A pointer to Ip6 multicast addresses to register.
|
||||
* @param aAddressNum The number of Ip6 multicast addresses.
|
||||
* @param aTimeout A pointer to the timeout (in seconds), or nullptr to use the default MLR timeout.
|
||||
* A timeout of 0 seconds removes the Multicast Listener addresses.
|
||||
* @param aCallback A callback function.
|
||||
* @param aContext A user context pointer.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully sent MLR.req. The @p aCallback will be called iff this method
|
||||
* returns OT_ERROR_NONE.
|
||||
* @retval OT_ERROR_BUSY If a previous registration was ongoing.
|
||||
* @retval OT_ERROR_INVALID_ARGS If one or more arguments are invalid.
|
||||
* @retval OT_ERROR_INVALID_STATE If the device was not in a valid state to send MLR.req (e.g. Commissioner not
|
||||
* started, Primary Backbone Router not found).
|
||||
* @retval OT_ERROR_NO_BUFS If insufficient message buffers available.
|
||||
*
|
||||
*/
|
||||
otError RegisterMulticastListeners(const otIp6Address * aAddresses,
|
||||
uint8_t aAddressNum,
|
||||
const uint32_t * aTimeout,
|
||||
otIp6RegisterMulticastListenersCallback aCallback,
|
||||
void * aContext);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void HandleNotifierEvents(Events aEvents);
|
||||
|
||||
void SendMulticastListenerRegistration(void);
|
||||
void SendMulticastListenerRegistration(void);
|
||||
otError SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses,
|
||||
uint8_t aAddressNum,
|
||||
const uint32_t * aTimeout,
|
||||
Coap::ResponseHandler aResponseHandler,
|
||||
void * aResponseContext);
|
||||
|
||||
static void HandleMulticastListenerRegistrationResponse(void * aContext,
|
||||
otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult)
|
||||
{
|
||||
static_cast<MlrManager *>(aContext)->HandleMulticastListenerRegistrationResponse(
|
||||
static_cast<Coap::Message *>(aMessage), static_cast<const Ip6::MessageInfo *>(aMessageInfo), aResult);
|
||||
}
|
||||
static void HandleMulticastListenerRegistrationResponse(void * aContext,
|
||||
otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult);
|
||||
void HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
|
||||
const Ip6::MessageInfo *aMessageInfo,
|
||||
otError aResult);
|
||||
static otError ParseMulticastListenerRegistrationResponse(otError aResult,
|
||||
Coap::Message *aMessage,
|
||||
uint8_t & aStatus,
|
||||
Ip6::Address * aFailedAddresses,
|
||||
uint8_t & aFailedAddressNum);
|
||||
|
||||
void HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage,
|
||||
const Ip6::MessageInfo *aMessageInfo,
|
||||
otError aResult);
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
static void HandleRegisterMulticastListenersResponse(void * aContext,
|
||||
otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult);
|
||||
void HandleRegisterMulticastListenersResponse(otMessage * aMessage,
|
||||
const otMessageInfo *aMessageInfo,
|
||||
otError aResult);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLR_ENABLE
|
||||
void UpdateLocalSubscriptions(void);
|
||||
@@ -156,17 +201,26 @@ private:
|
||||
void Reregister(void);
|
||||
void HandleTimeTick(void);
|
||||
|
||||
void LogMulticastAddresses(void);
|
||||
void CheckInvariants(void) const;
|
||||
void LogMlrResponse(otError aResult,
|
||||
otError aError,
|
||||
uint8_t aStatus,
|
||||
const Ip6::Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum);
|
||||
void LogMulticastAddresses(void);
|
||||
void CheckInvariants(void) const;
|
||||
static void LogMlrResponse(otError aResult,
|
||||
otError aError,
|
||||
uint8_t aStatus,
|
||||
const Ip6::Address *aFailedAddresses,
|
||||
uint8_t aFailedAddressNum);
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
otIp6RegisterMulticastListenersCallback mRegisterMulticastListenersCallback;
|
||||
void * mRegisterMulticastListenersContext;
|
||||
#endif
|
||||
|
||||
uint32_t mReregistrationDelay;
|
||||
uint16_t mSendDelay;
|
||||
bool mMlrPending : 1;
|
||||
|
||||
bool mMlrPending : 1;
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
|
||||
bool mRegisterMulticastListenersPending : 1;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ot
|
||||
|
||||
@@ -69,17 +69,19 @@ public:
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
kTarget = 0, ///< Target EID TLV
|
||||
kExtMacAddress = 1, ///< Extended MAC Address TLV
|
||||
kRloc16 = 2, ///< RLOC16 TLV
|
||||
kMeshLocalEid = 3, ///< ML-EID TLV
|
||||
kStatus = 4, ///< Status TLV
|
||||
kLastTransactionTime = 6, ///< Time Since Last Transaction TLV
|
||||
kRouterMask = 7, ///< Router Mask TLV
|
||||
kNDOption = 8, ///< ND Option TLV
|
||||
kNDData = 9, ///< ND Data TLV
|
||||
kThreadNetworkData = 10, ///< Thread Network Data TLV
|
||||
kIPv6Addresses = 14, ///< IPv6 Addresses TLV
|
||||
kTarget = 0, ///< Target EID TLV
|
||||
kExtMacAddress = 1, ///< Extended MAC Address TLV
|
||||
kRloc16 = 2, ///< RLOC16 TLV
|
||||
kMeshLocalEid = 3, ///< ML-EID TLV
|
||||
kStatus = 4, ///< Status TLV
|
||||
kLastTransactionTime = 6, ///< Time Since Last Transaction TLV
|
||||
kRouterMask = 7, ///< Router Mask TLV
|
||||
kNDOption = 8, ///< ND Option TLV
|
||||
kNDData = 9, ///< ND Data TLV
|
||||
kThreadNetworkData = 10, ///< Thread Network Data TLV
|
||||
kTimeout = 11, ///< Timeout TLV
|
||||
kIPv6Addresses = 14, ///< IPv6 Addresses TLV
|
||||
kCommissionerSessionId = 15, ///< Commissioner Session ID TLV
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -281,6 +281,37 @@ class Node:
|
||||
|
||||
return results
|
||||
|
||||
def _expect_command_output(self, cmd: str):
|
||||
lines = []
|
||||
cmd_output_started = False
|
||||
|
||||
while True:
|
||||
self._expect(r"[^\n]+")
|
||||
line = self.pexpect.match.group(0).decode('utf8').strip()
|
||||
|
||||
if line.startswith('> '):
|
||||
line = line[2:]
|
||||
|
||||
if line == '':
|
||||
continue
|
||||
|
||||
if line == cmd:
|
||||
cmd_output_started = True
|
||||
continue
|
||||
|
||||
if not cmd_output_started:
|
||||
continue
|
||||
|
||||
if line == 'Done':
|
||||
break
|
||||
elif line.startswith('Error '):
|
||||
raise Exception(line)
|
||||
else:
|
||||
lines.append(line)
|
||||
|
||||
print(f'_expect_command_output({cmd!r}) returns {lines!r}')
|
||||
return lines
|
||||
|
||||
def __init_soc(self, nodeid):
|
||||
""" Initialize a System-on-a-chip node connected via UART. """
|
||||
import fdpexpect
|
||||
@@ -564,6 +595,24 @@ class Node:
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def register_multicast_listener(self, *ipaddrs: Union[ipaddress.IPv6Address, str], timeout=None):
|
||||
assert len(ipaddrs) > 0, ipaddrs
|
||||
|
||||
cmd = f'mlr reg {" ".join(ipaddrs)}'
|
||||
if timeout is not None:
|
||||
cmd += f' {int(timeout)}'
|
||||
self.send_command(cmd)
|
||||
self.simulator.go(3)
|
||||
lines = self._expect_command_output(cmd)
|
||||
m = re.match(r'status (\d+), (\d+) failed', lines[0])
|
||||
assert m is not None, lines
|
||||
status = int(m.group(1))
|
||||
failed_num = int(m.group(2))
|
||||
assert failed_num == len(lines) - 1
|
||||
failed_ips = list(map(ipaddress.IPv6Address, lines[1:]))
|
||||
print(f"register_multicast_listener {ipaddrs} => status: {status}, failed ips: {failed_ips}")
|
||||
return status, failed_ips
|
||||
|
||||
def set_link_quality(self, addr, lqi):
|
||||
cmd = 'macfilter rss add-lqi %s %s' % (addr, lqi)
|
||||
self.send_command(cmd)
|
||||
|
||||
@@ -339,6 +339,104 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
self.flush_all()
|
||||
|
||||
def testCommissionerRegisterMulticastListeners(self):
|
||||
self._bootstrap()
|
||||
|
||||
# Use ROUTER_1_2 as the Commissioner
|
||||
commissioiner = self.nodes[ROUTER_1_2]
|
||||
|
||||
self.assertRaisesRegex(Exception, "InvalidState", lambda: commissioiner.register_multicast_listener("ff04::1"))
|
||||
|
||||
commissioiner.commissioner_start()
|
||||
self.simulator.go(10)
|
||||
|
||||
# Now the Commissioner should be able to register MAs
|
||||
for ip in ["ff04::1", "ff04::2"]:
|
||||
status, failed_ips = commissioiner.register_multicast_listener(ip)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
self.__check_multicast_listener(ip, expect_mlr_timeout_range=[290, 300])
|
||||
|
||||
# Register existing MA with a new timeout should be able to update the timeout
|
||||
for ip in ["ff04::1", "ff04::2"]:
|
||||
status, failed_ips = commissioiner.register_multicast_listener(ip, timeout=1000)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
self.__check_multicast_listener(ip, expect_mlr_timeout_range=[990, 1000])
|
||||
|
||||
# Register MAs with given timeouts
|
||||
for ip, timeout in [("ff05::1", 400), ("ff05::2", 500), ("ff05::3", 600)]:
|
||||
status, failed_ips = commissioiner.register_multicast_listener(ip, timeout=timeout)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
self.__check_multicast_listener(ip, expect_mlr_timeout_range=[timeout - 10, timeout])
|
||||
|
||||
# Register multiple MAs with one call
|
||||
ips = ["ff05::4", "ff05::5", "ff05::6"]
|
||||
status, failed_ips = commissioiner.register_multicast_listener(*ips, timeout=700)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
for ip in ips:
|
||||
self.__check_multicast_listener(ip, expect_mlr_timeout_range=[690, 700])
|
||||
|
||||
# Register multiple MAs with one call (without timeout)
|
||||
ips = ["ff05::7", "ff05::8", "ff05::9"]
|
||||
status, failed_ips = commissioiner.register_multicast_listener(*ips)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
for ip in ips:
|
||||
self.__check_multicast_listener(ip, expect_mlr_timeout_range=[290, 300])
|
||||
|
||||
# Unregister MAs using timeout=0
|
||||
for ip in ["ff05::1", "ff05::2", "ff05::3"]:
|
||||
status, failed_ips = commissioiner.register_multicast_listener(ip, timeout=0)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
self.__check_multicast_listener(ip, expect_not_present=True)
|
||||
|
||||
# Unregister multiple MAs
|
||||
ips = ["ff05::4", "ff05::5", "ff05::6"]
|
||||
status, failed_ips = commissioiner.register_multicast_listener(*ips, timeout=0)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
self.__check_multicast_listener(ip, expect_not_present=True)
|
||||
|
||||
# Remove MAs that are not subscribed should not fail
|
||||
ips = ["ff06::1", "ff02::1", "2001::1"]
|
||||
status, failed_ips = commissioiner.register_multicast_listener(*ips, timeout=0)
|
||||
self.assertTrue(status == 0 and not failed_ips)
|
||||
self.__check_multicast_listener(ip, expect_not_present=True)
|
||||
|
||||
# Register invalid MAs should fail
|
||||
for ip in ["ff02::1", "ff03::1", "2001::1"]:
|
||||
status, failed_ips = commissioiner.register_multicast_listener(ip)
|
||||
self.assertEqual(status, 2)
|
||||
self.assertEqual(set(failed_ips), {ipaddress.IPv6Address(ip)})
|
||||
self.__check_multicast_listener(ip, expect_not_present=True)
|
||||
|
||||
# Register valid MAs with invalid MAs should succeed partially
|
||||
ips = ["ff05::1", "ff05::2", "ff02::1", "2001::1"]
|
||||
status, failed_ips = commissioiner.register_multicast_listener(*ips)
|
||||
self.assertEqual(status, 2)
|
||||
self.assertEqual(set(failed_ips), {ipaddress.IPv6Address("ff02::1"), ipaddress.IPv6Address("2001::1")})
|
||||
self.__check_multicast_listener("ff05::1")
|
||||
self.__check_multicast_listener("ff05::2")
|
||||
self.__check_multicast_listener("ff02::1", expect_not_present=True)
|
||||
self.__check_multicast_listener("2001::1", expect_not_present=True)
|
||||
|
||||
# Registering persistent MAs should fail for now
|
||||
status, failed_ips = commissioiner.register_multicast_listener("ff06::1", timeout=0xffffffff)
|
||||
self.assertEqual(status, 3)
|
||||
# "ff06::1" should not be included in failed IPs because all IPs failed
|
||||
self.assertTrue(not failed_ips == 0)
|
||||
|
||||
def __check_multicast_listener(self, *addrs, expect_mlr_timeout_range=None, expect_not_present=False):
|
||||
addrs = map(ipaddress.IPv6Address, addrs)
|
||||
listeners = self.nodes[BBR_1].multicast_listener_list()
|
||||
logging.info("__check_multicast_listener get listeners: %s", listeners)
|
||||
|
||||
for addr in addrs:
|
||||
if not expect_not_present:
|
||||
self.assertIn(addr, listeners)
|
||||
if expect_mlr_timeout_range is not None:
|
||||
self.assertGreaterEqual(listeners[addr], expect_mlr_timeout_range[0])
|
||||
self.assertLessEqual(listeners[addr], expect_mlr_timeout_range[1])
|
||||
else:
|
||||
self.assertNotIn(addr, listeners)
|
||||
|
||||
def __test_multicast_listeners_table_api(self):
|
||||
self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user