diff --git a/include/openthread/commissioner.h b/include/openthread/commissioner.h index 00ed368d7..d81e043a0 100644 --- a/include/openthread/commissioner.h +++ b/include/openthread/commissioner.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -114,17 +115,31 @@ typedef struct otCommissioningDataset #define OT_PSKD_MAX_SIZE 32 ///< Size of a Joiner PSKd (bytes) +/** + * This enumeration defines a Joiner Info Typer. + * + */ +typedef enum otJoinerInfoType +{ + OT_JOINER_INFO_TYPE_ANY = 0, ///< Accept any Joiner (no EUI64 or Discerner is specified). + OT_JOINER_INFO_TYPE_EUI64 = 1, ///< Joiner EUI-64 is specified (`mSharedId.mEui64` in `otJoinerInfo`). + OT_JOINER_INFO_TYPE_DISCERNER = 2, ///< Joiner Discerner is specified (`mSharedId.mDiscerner` in `otJoinerInfo`). +} otJoinerInfoType; + /** * This structure represents a Joiner Info. * */ typedef struct otJoinerInfo { - otExtAddress mEui64; ///< Joiner eui64 - char mPsk[OT_PSKD_MAX_SIZE + 1]; ///< Joiner pskd - uint32_t mExpirationTime; ///< Joiner expiration time in msec - - bool mAny : 1; /// TRUE if eui64 isn't set, FALSE otherwise. + otJoinerInfoType mType; ///< Joiner type. + union + { + otExtAddress mEui64; ///< Joiner EUI64 (when `mType` is `OT_JOINER_INFO_TYPE_EUI64`) + otJoinerDiscerner mDiscerner; ///< Joiner Discerner (when `mType` is `OT_JOINER_INFO_TYPE_DISCERNER`) + } mSharedId; ///< Shared fields + char mPsk[OT_PSKD_MAX_SIZE + 1]; ///< Joiner PSKd + uint32_t mExpirationTime; ///< Joiner expiration time in msec } otJoinerInfo; /** @@ -141,12 +156,14 @@ typedef void (*otCommissionerStateCallback)(otCommissionerState aState, void *aC /** * This function pointer is called whenever the joiner state changes. * - * @param[in] aEvent The joiner event type. - * @param[in] aJoinerId A pointer to the Joiner ID. - * @param[in] aContext A pointer to application-specific context. + * @param[in] aEvent The joiner event type. + * @param[in] aJoinerInfo A pointer to the Joiner Info. + * @param[in] aJoinerId A pointer to the Joiner ID (if not known, it will be NULL). + * @param[in] aContext A pointer to application-specific context. * */ typedef void (*otCommissionerJoinerCallback)(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, const otExtAddress * aJoinerId, void * aContext); @@ -200,6 +217,27 @@ otError otCommissionerAddJoiner(otInstance * aInstance, const char * aPskd, uint32_t aTimeout); +/** + * This function adds a Joiner entry with a given Joiner Discerner value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDiscerner A pointer to the Joiner Discerner. + * @param[in] aPskd A pointer to the PSKd. + * @param[in] aTimeout A time after which a Joiner is automatically removed, in seconds. + * + * @retval OT_ERROR_NONE Successfully added the Joiner. + * @retval OT_ERROR_NO_BUFS No buffers available to add the Joiner. + * @retval OT_ERROR_INVALID_ARGS @p aDiscerner or @p aPskd is invalid. + * @retval OT_ERROR_INVALID_STATE The commissioner is not active. + * + * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). + * + */ +otError otCommissionerAddJoinerWithDiscerner(otInstance * aInstance, + const otJoinerDiscerner *aDiscerner, + const char * aPskd, + uint32_t aTimeout); + /** * This method get joiner info at aIterator position. * @@ -229,6 +267,22 @@ otError otCommissionerGetNextJoinerInfo(otInstance *aInstance, uint16_t *aIterat */ otError otCommissionerRemoveJoiner(otInstance *aInstance, const otExtAddress *aEui64); +/** + * This function removes a Joiner entry. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEui64 A pointer to the Joiner Discerner. + * + * @retval OT_ERROR_NONE Successfully removed the Joiner. + * @retval OT_ERROR_NOT_FOUND The Joiner specified by @p aEui64 was not found. + * @retval OT_ERROR_INVALID_ARGS @p aDiscerner is invalid. + * @retval OT_ERROR_INVALID_STATE The commissioner is not active. + * + * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). + * + */ +otError otCommissionerRemoveJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner); + /** * This function gets the Provisioning URL. * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 38f354f6a..2c8897e3b 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (7) +#define OPENTHREAD_API_VERSION (8) /** * @addtogroup api-instance diff --git a/include/openthread/joiner.h b/include/openthread/joiner.h index 13162cfd6..85df654cd 100644 --- a/include/openthread/joiner.h +++ b/include/openthread/joiner.h @@ -69,6 +69,18 @@ typedef enum otJoinerState OT_JOINER_STATE_JOINED = 5, } otJoinerState; +#define OT_JOINER_MAX_DISCERNER_LENGTH 64 ///< Maximum length of a Joiner Discerner in bits. + +/** + * This structure represents a Joiner Discerner. + * + */ +typedef struct otJoinerDiscerner +{ + uint64_t mValue; ///< Discerner value (the lowest `mLength` bits specify the discerner). + uint8_t mLength; ///< Length (number of bits) - must be non-zero and at most `OT_JOINER_MAX_DISCERNER_LENGTH`. +} otJoinerDiscerner; + /** * This function pointer is called to notify the completion of a join operation. * @@ -132,16 +144,48 @@ void otJoinerStop(otInstance *aInstance); otJoinerState otJoinerGetState(otInstance *aInstance); /** - * Get the Joiner ID. + * This method gets the Joiner ID. * - * Joiner ID is the first 64 bits of the result of computing SHA-256 over factory-assigned - * IEEE EUI-64, which is used as IEEE 802.15.4 Extended Address during commissioning process. + * If a Joiner Discerner is not set, Joiner ID is the first 64 bits of the result of computing SHA-256 over + * factory-assigned IEEE EUI-64. Otherwise the Joiner ID is calculated from the Joiner Discerner value. + * + * The Joiner ID is also used as the device's IEEE 802.15.4 Extended Address during commissioning process. * * @param[in] aInstance A pointer to the OpenThread instance. - * @param[out] aJoinerId A pointer to where the Joiner ID is placed. + * + * @returns A pointer to the Joiner ID. * */ -void otJoinerGetId(otInstance *aInstance, otExtAddress *aJoinerId); +const otExtAddress *otJoinerGetId(otInstance *aInstance); + +/** + * This method sets the Joiner Discerner. + * + * The Joiner Discerner is used to calculate the Joiner ID used during commissioning/joining process. + * + * By default (when a discerner is not provided or set to NULL), Joiner ID is derived as first 64 bits of the result + * of computing SHA-256 over factory-assigned IEEE EUI-64. Note that this is the main behavior expected by Thread + * specification. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aDiscerner A pointer to a Joiner Discerner. If NULL clears any previously set discerner. + * + * @retval OT_ERROR_NONE The Joiner Discerner updated successfully. + * @retval OT_ERROR_INVALID_ARGS @p aDisciminrator is not valid (specified length is not within valid range). + * @retval OT_ERROR_INVALID_STATE There is an ongoing Joining process so Joiner Discerner could not be changed. + * + */ +otError otJoinerSetDiscerner(otInstance *aInstance, otJoinerDiscerner *aDiscerner); + +/** + * This method gets the Joiner Discerner. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @returns A pointer to Joiner Discerner or NULL if none is set. + * + */ +const otJoinerDiscerner *otJoinerGetDiscerner(otInstance *aInstance); /** * @} diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp index c90128314..7aec85a6d 100644 --- a/src/cli/cli_commissioner.cpp +++ b/src/cli/cli_commissioner.cpp @@ -345,13 +345,20 @@ void Commissioner::HandleStateChanged(otCommissionerState aState) } } -void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, const otExtAddress *aJoinerId, void *aContext) +void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId, + void * aContext) { - static_cast(aContext)->HandleJoinerEvent(aEvent, aJoinerId); + static_cast(aContext)->HandleJoinerEvent(aEvent, aJoinerInfo, aJoinerId); } -void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, const otExtAddress *aJoinerId) +void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId) { + OT_UNUSED_VARIABLE(aJoinerInfo); + mInterpreter.mServer->OutputFormat("Commissioner: Joiner "); switch (aEvent) @@ -373,7 +380,10 @@ void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, const otE break; } - mInterpreter.OutputBytes(aJoinerId->m8, sizeof(*aJoinerId)); + if (aJoinerId != nullptr) + { + mInterpreter.OutputBytes(aJoinerId->m8, sizeof(*aJoinerId)); + } mInterpreter.mServer->OutputFormat("\r\n"); } diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp index 92ff28236..0b5b071a1 100644 --- a/src/cli/cli_commissioner.hpp +++ b/src/cli/cli_commissioner.hpp @@ -100,9 +100,12 @@ private: void HandleStateChanged(otCommissionerState aState); static void HandleJoinerEvent(otCommissionerJoinerEvent aJoinerEvent, + const otJoinerInfo * aJoinerInfo, const otExtAddress * aJoinerId, void * aContext); - void HandleJoinerEvent(otCommissionerJoinerEvent aJoinerEvent, const otExtAddress *aJoinerId); + void HandleJoinerEvent(otCommissionerJoinerEvent aJoinerEvent, + const otJoinerInfo * aJoinerInfo, + const otExtAddress * aJoinerId); static void HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp index f44cf1199..856cc33f2 100644 --- a/src/cli/cli_joiner.cpp +++ b/src/cli/cli_joiner.cpp @@ -66,11 +66,11 @@ otError Joiner::ProcessId(uint8_t aArgsLength, char *aArgs[]) OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - otExtAddress joinerId; + const otExtAddress *joinerId; - otJoinerGetId(mInterpreter.mInstance, &joinerId); + joinerId = otJoinerGetId(mInterpreter.mInstance); - mInterpreter.OutputBytes(joinerId.m8, sizeof(joinerId)); + mInterpreter.OutputBytes(joinerId->m8, sizeof(otExtAddress)); mInterpreter.mServer->OutputFormat("\r\n"); return OT_ERROR_NONE; diff --git a/src/core/api/commissioner_api.cpp b/src/core/api/commissioner_api.cpp index 07c741de9..4c0c1d532 100644 --- a/src/core/api/commissioner_api.cpp +++ b/src/core/api/commissioner_api.cpp @@ -46,33 +46,44 @@ otError otCommissionerStart(otInstance * aInstance, otCommissionerJoinerCallback aJoinerCallback, void * aCallbackContext) { - otError error; - Instance &instance = *static_cast(aInstance); - SuccessOrExit(error = - instance.Get().Start(aStateCallback, aJoinerCallback, aCallbackContext)); -exit: - return error; + return instance.Get().Start(aStateCallback, aJoinerCallback, aCallbackContext); } otError otCommissionerStop(otInstance *aInstance) { - otError error; Instance &instance = *static_cast(aInstance); - SuccessOrExit(error = instance.Get().Stop(/* aResign */ true)); + return instance.Get().Stop(/* aResign */ true); +} + +otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui64, const char *aPskd, uint32_t aTimeout) +{ + otError error; + MeshCoP::Commissioner &commissioner = static_cast(aInstance)->Get(); + + if (aEui64 == nullptr) + { + error = commissioner.AddJoinerAny(aPskd, aTimeout); + ExitNow(); + } + + error = commissioner.AddJoiner(*static_cast(aEui64), aPskd, aTimeout); exit: return error; } -otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui64, const char *aPskd, uint32_t aTimeout) +otError otCommissionerAddJoinerWithDiscerner(otInstance * aInstance, + const otJoinerDiscerner *aDiscerner, + const char * aPskd, + uint32_t aTimeout) { Instance &instance = *static_cast(aInstance); - return instance.Get().AddJoiner(static_cast(aEui64), aPskd, - aTimeout); + return instance.Get().AddJoiner(*static_cast(aDiscerner), + aPskd, aTimeout); } otError otCommissionerGetNextJoinerInfo(otInstance *aInstance, uint16_t *aIterator, otJoinerInfo *aJoiner) @@ -83,10 +94,28 @@ otError otCommissionerGetNextJoinerInfo(otInstance *aInstance, uint16_t *aIterat } otError otCommissionerRemoveJoiner(otInstance *aInstance, const otExtAddress *aEui64) +{ + otError error; + MeshCoP::Commissioner &commissioner = static_cast(aInstance)->Get(); + + if (aEui64 == nullptr) + { + error = commissioner.RemoveJoinerAny(/* aTimeout */ 0); + ExitNow(); + } + + error = commissioner.RemoveJoiner(*static_cast(aEui64), /* aTimeout */ 0); + +exit: + return error; +} + +otError otCommissionerRemoveJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner) { Instance &instance = *static_cast(aInstance); - return instance.Get().RemoveJoiner(static_cast(aEui64), 0); + return instance.Get().RemoveJoiner( + *static_cast(aDiscerner), 0); } otError otCommissionerSetProvisioningUrl(otInstance *aInstance, const char *aProvisioningUrl) diff --git a/src/core/api/joiner_api.cpp b/src/core/api/joiner_api.cpp index c2e1c4927..0b35e2604 100644 --- a/src/core/api/joiner_api.cpp +++ b/src/core/api/joiner_api.cpp @@ -71,10 +71,35 @@ otJoinerState otJoinerGetState(otInstance *aInstance) return instance.Get().GetState(); } -void otJoinerGetId(otInstance *aInstance, otExtAddress *aJoinerId) +const otExtAddress *otJoinerGetId(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); - instance.Get().GetJoinerId(*static_cast(aJoinerId)); + return &instance.Get().GetId(); } + +otError otJoinerSetDiscerner(otInstance *aInstance, otJoinerDiscerner *aDiscerner) +{ + otError error = OT_ERROR_NONE; + MeshCoP::Joiner &joiner = static_cast(aInstance)->Get(); + + if (aDiscerner != NULL) + { + error = joiner.SetDiscerner(*static_cast(aDiscerner)); + } + else + { + error = joiner.ClearDiscerner(); + } + + return error; +} + +const otJoinerDiscerner *otJoinerGetDiscerner(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.Get().GetDiscerner(); +} + #endif // OPENTHREAD_CONFIG_JOINER_ENABLE diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index cf6872f3a..9df9481db 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -57,10 +57,10 @@ namespace MeshCoP { Commissioner::Commissioner(Instance &aInstance) : InstanceLocator(aInstance) + , mActiveJoiner(nullptr) , mJoinerPort(0) , mJoinerRloc(0) , mSessionId(0) - , mJoinerIndex(0) , mTransmitAttempts(0) , mJoinerExpirationTimer(aInstance, HandleJoinerExpirationTimer, this) , mTimer(aInstance, HandleTimer, this) @@ -70,10 +70,10 @@ Commissioner::Commissioner(Instance &aInstance) , mAnnounceBegin(aInstance) , mEnergyScan(aInstance) , mPanIdQuery(aInstance) + , mState(OT_COMMISSIONER_STATE_DISABLED) , mStateCallback(nullptr) , mJoinerCallback(nullptr) , mCallbackContext(nullptr) - , mState(OT_COMMISSIONER_STATE_DISABLED) { memset(mJoiners, 0, sizeof(mJoiners)); @@ -105,12 +105,34 @@ exit: return; } -void Commissioner::SignalJoinerEvent(otCommissionerJoinerEvent aEvent, const Mac::ExtAddress &aJoinerId) +void Commissioner::SignalJoinerEvent(otCommissionerJoinerEvent aEvent, const Joiner *aJoiner) const { - if (mJoinerCallback) + otJoinerInfo joinerInfo; + Mac::ExtAddress joinerId; + bool noJoinerId = false; + + VerifyOrExit((mJoinerCallback != nullptr) && (aJoiner != nullptr), OT_NOOP); + + aJoiner->CopyToJoinerInfo(joinerInfo); + + if (aJoiner->mType == Joiner::kTypeEui64) { - mJoinerCallback(aEvent, &aJoinerId, mCallbackContext); + ComputeJoinerId(aJoiner->mSharedId.mEui64, joinerId); } + else if (aJoiner == mActiveJoiner) + { + joinerId.Set(mJoinerIid.m8); + joinerId.ToggleLocal(); + } + else + { + noJoinerId = true; + } + + mJoinerCallback(aEvent, &joinerInfo, noJoinerId ? nullptr : &joinerId, mCallbackContext); + +exit: + return; } void Commissioner::AddCoapResources(void) @@ -134,15 +156,7 @@ void Commissioner::HandleCoapsConnected(bool aConnected, void *aContext) void Commissioner::HandleCoapsConnected(bool aConnected) { - otCommissionerJoinerEvent event; - Mac::ExtAddress joinerId; - - event = aConnected ? OT_COMMISSIONER_JOINER_CONNECTED : OT_COMMISSIONER_JOINER_END; - - joinerId.Set(mJoinerIid.m8); - joinerId.ToggleLocal(); - - SignalJoinerEvent(event, joinerId); + SignalJoinerEvent(aConnected ? OT_COMMISSIONER_JOINER_CONNECTED : OT_COMMISSIONER_JOINER_END, mActiveJoiner); } Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void) @@ -151,7 +165,7 @@ Commissioner::Joiner *Commissioner::GetUnusedJoinerEntry(void) for (joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) { - if (!joiner->mValid) + if (joiner->mType == Joiner::kTypeUnused) { ExitNow(); } @@ -169,24 +183,43 @@ Commissioner::Joiner *Commissioner::FindJoinerEntry(const Mac::ExtAddress *aEui6 for (joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) { - if (!joiner->mValid) + switch (joiner->mType) { - continue; - } + case Joiner::kTypeUnused: + case Joiner::kTypeDiscerner: + break; - if (aEui64 == nullptr) - { - if (joiner->mAny) + case Joiner::kTypeAny: + if (aEui64 == nullptr) { ExitNow(); } + break; + + case Joiner::kTypeEui64: + if ((aEui64 != nullptr) && (joiner->mSharedId.mEui64 == *aEui64)) + { + ExitNow(); + } + break; } - else + } + + joiner = nullptr; + +exit: + return joiner; +} + +Commissioner::Joiner *Commissioner::FindJoinerEntry(const JoinerDiscerner &aDiscerner) +{ + Joiner *joiner; + + for (joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) + { + if ((joiner->mType == Joiner::kTypeDiscerner) && (aDiscerner == joiner->mSharedId.mDiscerner)) { - if (!joiner->mAny && (joiner->mEui64 == *aEui64)) - { - ExitNow(); - } + ExitNow(); } } @@ -198,32 +231,45 @@ exit: Commissioner::Joiner *Commissioner::FindBestMatchingJoinerEntry(const Mac::ExtAddress &aReceivedJoinerId) { - Joiner *best = nullptr; + Joiner * best = nullptr; + Mac::ExtAddress joinerId; // Prefer a full Joiner ID match, if not found use the entry // accepting any joiner. for (Joiner *joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) { - if (!joiner->mValid) + switch (joiner->mType) { - continue; - } + case Joiner::kTypeUnused: + break; - if (!joiner->mAny) - { - Mac::ExtAddress joinerId; - - ComputeJoinerId(joiner->mEui64, joinerId); + case Joiner::kTypeAny: + if (best == nullptr) + { + best = joiner; + } + break; + case Joiner::kTypeEui64: + ComputeJoinerId(joiner->mSharedId.mEui64, joinerId); if (joinerId == aReceivedJoinerId) { ExitNow(best = joiner); } - } - else - { - best = joiner; + break; + + case Joiner::kTypeDiscerner: + if (joiner->mSharedId.mDiscerner.Matches(aReceivedJoinerId)) + { + if ((best == nullptr) || + ((best->mType == Joiner::kTypeDiscerner) && + (best->mSharedId.mDiscerner.GetLength() < joiner->mSharedId.mDiscerner.GetLength()))) + { + best = joiner; + } + } + break; } } @@ -233,16 +279,25 @@ exit: void Commissioner::RemoveJoinerEntry(Commissioner::Joiner &aJoiner) { - Mac::ExtAddress joinerId; + // Create a copy of `aJoiner` to use for signaling joiner event + // and logging after the entry is removed. This ensures the joiner + // event callback is invoked after all states are cleared. + + Joiner joinerCopy = aJoiner; + + aJoiner.mType = Joiner::kTypeUnused; + + if (&aJoiner == mActiveJoiner) + { + mActiveJoiner = nullptr; + } - aJoiner.mValid = false; UpdateJoinerExpirationTimer(); SendCommissionerSet(); - LogJoinerEntry("Removed", aJoiner); - ComputeJoinerId(aJoiner.mEui64, joinerId); - SignalJoinerEvent(OT_COMMISSIONER_JOINER_REMOVED, joinerId); + LogJoinerEntry("Removed", joinerCopy); + SignalJoinerEvent(OT_COMMISSIONER_JOINER_REMOVED, &joinerCopy); } otError Commissioner::Start(otCommissionerStateCallback aStateCallback, @@ -322,12 +377,42 @@ exit: return error; } +void Commissioner::ComputeBloomFilter(SteeringData &aSteeringData) const +{ + Mac::ExtAddress joinerId; + + aSteeringData.Init(); + + for (const Joiner *joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) + { + switch (joiner->mType) + { + case Joiner::kTypeUnused: + break; + + case Joiner::kTypeEui64: + ComputeJoinerId(joiner->mSharedId.mEui64, joinerId); + aSteeringData.UpdateBloomFilter(joinerId); + break; + + case Joiner::kTypeDiscerner: + aSteeringData.UpdateBloomFilter(joiner->mSharedId.mDiscerner); + break; + + case Joiner::kTypeAny: + aSteeringData.SetToPermitAllJoiners(); + ExitNow(); + } + } + +exit: + return; +} + void Commissioner::SendCommissionerSet(void) { otError error = OT_ERROR_NONE; otCommissioningDataset dataset; - SteeringData & steeringData = static_cast(dataset.mSteeringData); - Mac::ExtAddress joinerId; VerifyOrExit(mState == OT_COMMISSIONER_STATE_ACTIVE, error = OT_ERROR_INVALID_STATE); @@ -336,26 +421,7 @@ void Commissioner::SendCommissionerSet(void) dataset.mSessionId = mSessionId; dataset.mIsSessionIdSet = true; - // Compute bloom filter - steeringData.Init(); - - for (Joiner *joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) - { - if (!joiner->mValid) - { - continue; - } - - if (joiner->mAny) - { - steeringData.SetToPermitAllJoiners(); - break; - } - - ComputeJoinerId(joiner->mEui64, joinerId); - steeringData.UpdateBloomFilter(joinerId); - } - + ComputeBloomFilter(static_cast(dataset.mSteeringData)); dataset.mIsSteeringDataSet = true; error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0); @@ -371,13 +437,16 @@ void Commissioner::ClearJoiners(void) { for (Joiner *joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) { - joiner->mValid = false; + joiner->mType = Joiner::kTypeUnused; } SendCommissionerSet(); } -otError Commissioner::AddJoiner(const Mac::ExtAddress *aEui64, const char *aPskd, uint32_t aTimeout) +otError Commissioner::AddJoiner(const Mac::ExtAddress *aEui64, + const JoinerDiscerner *aDiscerner, + const char * aPskd, + uint32_t aTimeout) { otError error = OT_ERROR_NONE; Joiner *joiner; @@ -385,7 +454,15 @@ otError Commissioner::AddJoiner(const Mac::ExtAddress *aEui64, const char *aPskd VerifyOrExit(mState == OT_COMMISSIONER_STATE_ACTIVE, error = OT_ERROR_INVALID_STATE); VerifyOrExit(IsPskdValid(aPskd), error = OT_ERROR_INVALID_ARGS); - joiner = FindJoinerEntry(aEui64); + if (aDiscerner != nullptr) + { + VerifyOrExit(aDiscerner->IsValid(), error = OT_ERROR_INVALID_ARGS); + joiner = FindJoinerEntry(*aDiscerner); + } + else + { + joiner = FindJoinerEntry(aEui64); + } if (joiner == nullptr) { @@ -394,18 +471,24 @@ otError Commissioner::AddJoiner(const Mac::ExtAddress *aEui64, const char *aPskd VerifyOrExit(joiner != nullptr, error = OT_ERROR_NO_BUFS); - if (aEui64 != nullptr) + if (aDiscerner != nullptr) { - joiner->mAny = false; - joiner->mEui64 = *aEui64; + joiner->mType = Joiner::kTypeDiscerner; + joiner->mSharedId.mDiscerner = *aDiscerner; + } + else if (aEui64 != nullptr) + { + joiner->mType = Joiner::kTypeEui64; + joiner->mSharedId.mEui64 = *aEui64; } else { - joiner->mAny = true; + joiner->mType = Joiner::kTypeAny; } strncpy(joiner->mPsk, aPskd, sizeof(joiner->mPsk) - 1); - joiner->mValid = true; + joiner->mPsk[sizeof(joiner->mPsk) - 1] = '\0'; + joiner->mExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aTimeout); UpdateJoinerExpirationTimer(); @@ -418,26 +501,50 @@ exit: return error; } -otError Commissioner::GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aJoiner) const +void Commissioner::Joiner::CopyToJoinerInfo(otJoinerInfo &aInfo) const +{ + memset(&aInfo, 0, sizeof(aInfo)); + + switch (mType) + { + case kTypeAny: + aInfo.mType = OT_JOINER_INFO_TYPE_ANY; + break; + + case kTypeEui64: + aInfo.mType = OT_JOINER_INFO_TYPE_EUI64; + aInfo.mSharedId.mEui64 = mSharedId.mEui64; + break; + + case kTypeDiscerner: + aInfo.mType = OT_JOINER_INFO_TYPE_DISCERNER; + aInfo.mSharedId.mDiscerner = mSharedId.mDiscerner; + break; + + case kTypeUnused: + ExitNow(); + } + + strncpy(aInfo.mPsk, mPsk, sizeof(aInfo.mPsk) - 1); + aInfo.mExpirationTime = mExpirationTime - TimerMilli::GetNow(); + +exit: + return; +} + +otError Commissioner::GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aInfo) const { otError error = OT_ERROR_NONE; - size_t index; - for (index = aIterator; index < OT_ARRAY_LENGTH(mJoiners); index++) + while (aIterator < OT_ARRAY_LENGTH(mJoiners)) { - if (!mJoiners[index].mValid) + const Joiner &joiner = mJoiners[aIterator++]; + + if (joiner.mType != Joiner::kTypeUnused) { - continue; + joiner.CopyToJoinerInfo(aInfo); + ExitNow(); } - - memset(&aJoiner, 0, sizeof(aJoiner)); - - aJoiner.mAny = mJoiners[index].mAny; - aJoiner.mEui64 = mJoiners[index].mEui64; - strncpy(aJoiner.mPsk, mJoiners[index].mPsk, sizeof(aJoiner.mPsk) - 1); - aJoiner.mExpirationTime = mJoiners[index].mExpirationTime - TimerMilli::GetNow(); - aIterator = static_cast(index) + 1; - ExitNow(); } error = OT_ERROR_NOT_FOUND; @@ -446,33 +553,47 @@ exit: return error; } -otError Commissioner::RemoveJoiner(const Mac::ExtAddress *aEui64, uint32_t aDelay) +otError Commissioner::RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay) { otError error = OT_ERROR_NONE; Joiner *joiner; VerifyOrExit(mState == OT_COMMISSIONER_STATE_ACTIVE, error = OT_ERROR_INVALID_STATE); - joiner = FindJoinerEntry(aEui64); + if (aDiscerner != nullptr) + { + VerifyOrExit(aDiscerner->IsValid(), error = OT_ERROR_INVALID_ARGS); + joiner = FindJoinerEntry(*aDiscerner); + } + else + { + joiner = FindJoinerEntry(aEui64); + } + VerifyOrExit(joiner != nullptr, error = OT_ERROR_NOT_FOUND); + RemoveJoiner(*joiner, aDelay); + +exit: + return error; +} + +void Commissioner::RemoveJoiner(Joiner &aJoiner, uint32_t aDelay) +{ if (aDelay > 0) { TimeMilli newExpirationTime = TimerMilli::GetNow() + Time::SecToMsec(aDelay); - if (joiner->mExpirationTime > newExpirationTime) + if (aJoiner.mExpirationTime > newExpirationTime) { - joiner->mExpirationTime = newExpirationTime; + aJoiner.mExpirationTime = newExpirationTime; UpdateJoinerExpirationTimer(); } } else { - RemoveJoinerEntry(*joiner); + RemoveJoinerEntry(aJoiner); } - -exit: - return error; } otError Commissioner::SetProvisioningUrl(const char *aProvisioningUrl) @@ -530,7 +651,12 @@ void Commissioner::HandleJoinerExpirationTimer(void) for (Joiner *joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) { - if (joiner->mValid && (joiner->mExpirationTime <= now)) + if (joiner->mType == Joiner::kTypeUnused) + { + continue; + } + + if (joiner->mExpirationTime <= now) { otLogDebgMeshCoP("removing joiner due to timeout or successfully joined"); RemoveJoinerEntry(*joiner); @@ -547,7 +673,7 @@ void Commissioner::UpdateJoinerExpirationTimer(void) for (Joiner *joiner = &mJoiners[0]; joiner < OT_ARRAY_END(mJoiners); joiner++) { - if (!joiner->mValid) + if (joiner->mType == Joiner::kTypeUnused) { continue; } @@ -939,10 +1065,10 @@ void Commissioner::HandleRelayReceive(Coap::Message &aMessage, const Ip6::Messag SuccessOrExit(error = Get().SetPsk(reinterpret_cast(joiner->mPsk), static_cast(strlen(joiner->mPsk)))); - mJoinerIndex = static_cast(joiner - mJoiners); + mActiveJoiner = joiner; LogJoinerEntry("Starting new session with", *joiner); - SignalJoinerEvent(OT_COMMISSIONER_JOINER_START, receivedId); + SignalJoinerEvent(OT_COMMISSIONER_JOINER_START, joiner); } else { @@ -1032,7 +1158,6 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State otError error = OT_ERROR_NONE; Ip6::MessageInfo joinerMessageInfo; Coap::Message * message; - Mac::ExtAddress joinerId; VerifyOrExit((message = NewMeshCoPMessage(Get())) != nullptr, error = OT_ERROR_NO_BUFS); @@ -1057,14 +1182,12 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State SuccessOrExit(error = Get().SendMessage(*message, joinerMessageInfo)); - joinerId.Set(mJoinerIid.m8); - joinerId.ToggleLocal(); - SignalJoinerEvent(OT_COMMISSIONER_JOINER_FINALIZE, joinerId); + SignalJoinerEvent(OT_COMMISSIONER_JOINER_FINALIZE, mActiveJoiner); - if (!mJoiners[mJoinerIndex].mAny) + if ((mActiveJoiner != nullptr) && (mActiveJoiner->mType != Joiner::kTypeAny)) { - // remove after kRemoveJoinerDelay (seconds) - IgnoreError(RemoveJoiner(&mJoiners[mJoinerIndex].mEui64, kRemoveJoinerDelay)); + // Remove after kRemoveJoinerDelay (seconds) + RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay); } otLogInfoMeshCoP("sent joiner finalize response"); @@ -1173,8 +1296,25 @@ const char *Commissioner::StateToString(otCommissionerState aState) void Commissioner::LogJoinerEntry(const char *aAction, const Joiner &aJoiner) const { - otLogInfoMeshCoP("%s Joiner (%s, %s)", aAction, aJoiner.mAny ? "*" : aJoiner.mEui64.ToString().AsCString(), - aJoiner.mPsk); + switch (aJoiner.mType) + { + case Joiner::kTypeUnused: + break; + + case Joiner::kTypeAny: + otLogInfoMeshCoP("%s Joiner (any, %s)", aAction, aJoiner.mPsk); + break; + + case Joiner::kTypeEui64: + otLogInfoMeshCoP("%s Joiner (eui64:%s, %s)", aAction, aJoiner.mSharedId.mEui64.ToString().AsCString(), + aJoiner.mPsk); + break; + + case Joiner::kTypeDiscerner: + otLogInfoMeshCoP("%s Joiner (disc:%s, %s)", aAction, aJoiner.mSharedId.mDiscerner.ToString().AsCString(), + aJoiner.mPsk); + break; + } } #else diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp index 2318bd5ec..44e77bfb1 100644 --- a/src/core/meshcop/commissioner.hpp +++ b/src/core/meshcop/commissioner.hpp @@ -101,9 +101,8 @@ public: void ClearJoiners(void); /** - * This method adds a Joiner entry. + * This method adds a Joiner entry accepting any Joiner. * - * @param[in] aEui64 A pointer to the Joiner's IEEE EUI-64 or nullptr for any Joiner. * @param[in] aPskd A pointer to the PSKd. * @param[in] aTimeout A time after which a Joiner is automatically removed, in seconds. * @@ -112,7 +111,41 @@ public: * @retval OT_ERROR_INVALID_STATE Commissioner service is not started. * */ - otError AddJoiner(const Mac::ExtAddress *aEui64, const char *aPskd, uint32_t aTimeout); + otError AddJoinerAny(const char *aPskd, uint32_t aTimeout) { return AddJoiner(nullptr, nullptr, aPskd, aTimeout); } + + /** + * This method adds a Joiner entry. + * + * @param[in] aEui64 The Joiner's IEEE EUI-64. + * @param[in] aPskd A pointer to the PSKd. + * @param[in] aTimeout A time after which a Joiner is automatically removed, in seconds. + * + * @retval OT_ERROR_NONE Successfully added the Joiner. + * @retval OT_ERROR_NO_BUFS No buffers available to add the Joiner. + * @retval OT_ERROR_INVALID_STATE Commissioner service is not started. + * + */ + otError AddJoiner(const Mac::ExtAddress &aEui64, const char *aPskd, uint32_t aTimeout) + { + return AddJoiner(&aEui64, nullptr, aPskd, aTimeout); + } + + /** + * This method adds a Joiner entry with a Joiner Discerner. + * + * @param[in] aDiscerner A Joiner Discerner. + * @param[in] aPskd A pointer to the PSKd. + * @param[in] aTimeout A time after which a Joiner is automatically removed, in seconds. + * + * @retval OT_ERROR_NONE Successfully added the Joiner. + * @retval OT_ERROR_NO_BUFS No buffers available to add the Joiner. + * @retval OT_ERROR_INVALID_STATE Commissioner service is not started. + * + */ + otError AddJoiner(const JoinerDiscerner &aDiscerner, const char *aPskd, uint32_t aTimeout) + { + return AddJoiner(nullptr, &aDiscerner, aPskd, aTimeout); + } /** * This method get joiner info at aIterator position. @@ -126,10 +159,22 @@ public: */ otError GetNextJoinerInfo(uint16_t &aIterator, otJoinerInfo &aJoiner) const; + /** + * This method removes a Joiner entry accepting any Joiner. + * + * @param[in] aDelay The delay to remove Joiner (in seconds). + * + * @retval OT_ERROR_NONE Successfully added the Joiner. + * @retval OT_ERROR_NOT_FOUND The Joiner entry accepting any Joiner was not found. + * @retval OT_ERROR_INVALID_STATE Commissioner service is not started. + * + */ + otError RemoveJoinerAny(uint32_t aDelay) { return RemoveJoiner(nullptr, nullptr, aDelay); } + /** * This method removes a Joiner entry. * - * @param[in] aEui64 A pointer to the Joiner's IEEE EUI-64 or nullptr for any Joiner. + * @param[in] aEui64 The Joiner's IEEE EUI-64. * @param[in] aDelay The delay to remove Joiner (in seconds). * * @retval OT_ERROR_NONE Successfully added the Joiner. @@ -137,7 +182,26 @@ public: * @retval OT_ERROR_INVALID_STATE Commissioner service is not started. * */ - otError RemoveJoiner(const Mac::ExtAddress *aEui64, uint32_t aDelay); + otError RemoveJoiner(const Mac::ExtAddress &aEui64, uint32_t aDelay) + { + return RemoveJoiner(&aEui64, nullptr, aDelay); + } + + /** + * This method removes a Joiner entry. + * + * @param[in] aDiscerner A Joiner Discerner. + * @param[in] aDelay The delay to remove Joiner (in seconds). + * + * @retval OT_ERROR_NONE Successfully added the Joiner. + * @retval OT_ERROR_NOT_FOUND The Joiner specified by @p aEui64 was not found. + * @retval OT_ERROR_INVALID_STATE Commissioner service is not started. + * + */ + otError RemoveJoiner(const JoinerDiscerner &aDiscerner, uint32_t aDelay) + { + return RemoveJoiner(nullptr, &aDiscerner, aDelay); + } /** * This method gets the Provisioning URL. @@ -265,18 +329,41 @@ private: struct Joiner { - Mac::ExtAddress mEui64; - TimeMilli mExpirationTime; - char mPsk[Dtls::kPskMaxLength + 1]; - bool mValid : 1; - bool mAny : 1; + enum Type + { + kTypeUnused = 0, // Need to be 0 to ensure `memset()` clears all `Joiners` + kTypeAny, + kTypeEui64, + kTypeDiscerner, + }; + + TimeMilli mExpirationTime; + + union + { + Mac::ExtAddress mEui64; + JoinerDiscerner mDiscerner; + } mSharedId; + + char mPsk[Dtls::kPskMaxLength + 1]; + Type mType; + + void CopyToJoinerInfo(otJoinerInfo &aInfo) const; }; Joiner *GetUnusedJoinerEntry(void); Joiner *FindJoinerEntry(const Mac::ExtAddress *aEui64); + Joiner *FindJoinerEntry(const JoinerDiscerner &aDiscerner); Joiner *FindBestMatchingJoinerEntry(const Mac::ExtAddress &aRxJoinerId); void RemoveJoinerEntry(Joiner &aJoiner); + otError AddJoiner(const Mac::ExtAddress *aEui64, + const JoinerDiscerner *aDiscerner, + const char * aPskd, + uint32_t aTimeout); + otError RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay); + void RemoveJoiner(Joiner &aJoiner, uint32_t aDelay); + void AddCoapResources(void); void RemoveCoapResources(void); @@ -330,24 +417,25 @@ private: static otError SendRelayTransmit(void *aContext, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); otError SendRelayTransmit(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void ComputeBloomFilter(SteeringData &aSteeringData) const; void SendCommissionerSet(void); otError SendPetition(void); void SendKeepAlive(void); void SendKeepAlive(uint16_t aSessionId); void SetState(otCommissionerState aState); - void SignalJoinerEvent(otCommissionerJoinerEvent aEvent, const Mac::ExtAddress &aJoinerId); + void SignalJoinerEvent(otCommissionerJoinerEvent aEvent, const Joiner *aJoiner) const; void LogJoinerEntry(const char *aAction, const Joiner &aJoiner) const; static const char *StateToString(otCommissionerState aState); Joiner mJoiners[OPENTHREAD_CONFIG_COMMISSIONER_MAX_JOINER_ENTRIES]; + Joiner * mActiveJoiner; Ip6::InterfaceIdentifier mJoinerIid; uint16_t mJoinerPort; uint16_t mJoinerRloc; uint16_t mSessionId; - uint8_t mJoinerIndex; uint8_t mTransmitAttempts; TimerMilli mJoinerExpirationTimer; TimerMilli mTimer; @@ -364,11 +452,11 @@ private: char mProvisioningUrl[OT_PROVISIONING_URL_MAX_SIZE + 1]; // + 1 is for null char at end of string. + otCommissionerState mState; + otCommissionerStateCallback mStateCallback; otCommissionerJoinerCallback mJoinerCallback; void * mCallbackContext; - - otCommissionerState mState; }; } // namespace MeshCoP diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 33da42cbe..92640e85e 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -57,6 +57,8 @@ namespace MeshCoP { Joiner::Joiner(Instance &aInstance) : InstanceLocator(aInstance) + , mId() + , mDiscerner() , mState(OT_JOINER_STATE_IDLE) , mCallback(nullptr) , mContext(nullptr) @@ -65,14 +67,51 @@ Joiner::Joiner(Instance &aInstance) , mTimer(aInstance, Joiner::HandleTimer, this) , mJoinerEntrust(OT_URI_PATH_JOINER_ENTRUST, &Joiner::HandleJoinerEntrust, this) { + SetIdFromIeeeEui64(); + mDiscerner.Clear(); memset(mJoinerRouters, 0, sizeof(mJoinerRouters)); Get().AddResource(mJoinerEntrust); } -void Joiner::GetJoinerId(Mac::ExtAddress &aJoinerId) const +void Joiner::SetIdFromIeeeEui64(void) { - Get().GetIeeeEui64(aJoinerId); - ComputeJoinerId(aJoinerId, aJoinerId); + Mac::ExtAddress eui64; + + Get().GetIeeeEui64(eui64); + ComputeJoinerId(eui64, mId); +} + +const JoinerDiscerner *Joiner::GetDiscerner(void) const +{ + return mDiscerner.IsEmpty() ? nullptr : &mDiscerner; +} + +otError Joiner::SetDiscerner(const JoinerDiscerner &aDiscerner) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aDiscerner.IsValid(), error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(mState == OT_JOINER_STATE_IDLE, error = OT_ERROR_INVALID_STATE); + + mDiscerner = aDiscerner; + mDiscerner.GenerateJoinerId(mId); + +exit: + return error; +} + +otError Joiner::ClearDiscerner(void) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(mState == OT_JOINER_STATE_IDLE, error = OT_ERROR_INVALID_STATE); + VerifyOrExit(!mDiscerner.IsEmpty(), OT_NOOP); + + mDiscerner.Clear(); + SetIdFromIeeeEui64(); + +exit: + return error; } void Joiner::SetState(otJoinerState aState) @@ -96,8 +135,9 @@ otError Joiner::Start(const char * aPskd, otJoinerCallback aCallback, void * aContext) { - otError error; - Mac::ExtAddress randomAddress; + otError error; + Mac::ExtAddress randomAddress; + SteeringData::HashBitIndexes filterIndexes; otLogInfoMeshCoP("Joiner starting"); @@ -122,10 +162,18 @@ otError Joiner::Start(const char * aPskd, SuccessOrExit(error = PrepareJoinerFinalizeMessage(aProvisioningUrl, aVendorName, aVendorModel, aVendorSwVersion, aVendorData)); + if (!mDiscerner.IsEmpty()) + { + SteeringData::CalculateHashBitIndexes(mDiscerner, filterIndexes); + } + else + { + SteeringData::CalculateHashBitIndexes(mId, filterIndexes); + } + SuccessOrExit(error = Get().Discover(Mac::ChannelMask(0), Get().GetPanId(), /* aJoiner */ true, /* aEnableFiltering */ true, - /* aFilterIndexes (use hash of factory EUI64) */ nullptr, - HandleDiscoverResult, this)); + &filterIndexes, HandleDiscoverResult, this)); mCallback = aCallback; mContext = aContext; @@ -226,8 +274,6 @@ void Joiner::HandleDiscoverResult(otActiveScanResult *aResult, void *aContext) void Joiner::HandleDiscoverResult(otActiveScanResult *aResult) { - Mac::ExtAddress joinerId; - VerifyOrExit(mState == OT_JOINER_STATE_DISCOVER, OT_NOOP); if (aResult != nullptr) @@ -236,9 +282,7 @@ void Joiner::HandleDiscoverResult(otActiveScanResult *aResult) } else { - // Use extended address based on factory-assigned IEEE EUI-64 - GetJoinerId(joinerId); - Get().SetExtAddress(joinerId); + Get().SetExtAddress(mId); Get().UpdateLinkLocalAddress(); mJoinerRouterIndex = 0; diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp index f0ef074eb..8078915df 100644 --- a/src/core/meshcop/joiner.hpp +++ b/src/core/meshcop/joiner.hpp @@ -46,6 +46,7 @@ #include "common/message.hpp" #include "mac/mac_types.hpp" #include "meshcop/dtls.hpp" +#include "meshcop/meshcop.hpp" #include "meshcop/meshcop_tlvs.hpp" namespace ot { @@ -104,10 +105,47 @@ public: /** * This method retrieves the Joiner ID. * - * @param[out] aJoinerId The Joiner ID. + * @returns The Joiner ID. * */ - void GetJoinerId(Mac::ExtAddress &aJoinerId) const; + const Mac::ExtAddress &GetId(void) const { return mId; } + + /** + * This method gets the Jointer Discerner. + * + * @returns A pointer to the current Joiner Discerner or `nullptr` if none is set. + * + */ + const JoinerDiscerner *GetDiscerner(void) const; + + /** + * This method sets the Joiner Discerner. + * + * The Joiner Discerner is used to calculate the Joiner ID used during commissioning/joining process. + * + * By default (when a discerner is not provided or cleared), Joiner ID is derived as first 64 bits of the + * result of computing SHA-256 over factory-assigned IEEE EUI-64. Note that this is the main behavior expected by + * Thread specification. + * + * @param[in] aDiscerner A Joiner Discerner + * + * @retval OT_ERROR_NONE The Joiner Discerner updated successfully. + * @retval OT_ERROR_INVALID_ARGS @p aDisciminrator is not valid (specified length is not within valid range). + * @retval OT_ERROR_INVALID_STATE There is an ongoing Joining process so Joiner Discerner could not be changed. + * + */ + otError SetDiscerner(const JoinerDiscerner &aDiscerner); + + /** + * This method clears any previously set Joiner Discerner. + * + * When cleared, Joiner ID is derived as first 64 bits of SHA-256 of factory-assigned IEEE EUI-64. + * + * @retval OT_ERROR_NONE The Joiner Discerner cleared and Joiner ID updated. + * @retval OT_ERROR_INVALID_STATE There is an ongoing Joining process so Joiner Discerner could not be changed. + * + */ + otError ClearDiscerner(void); private: enum @@ -147,6 +185,7 @@ private: static const char *JoinerStateToString(otJoinerState aState); void SetState(otJoinerState aState); + void SetIdFromIeeeEui64(void); void SaveDiscoveredJoinerRouter(const otActiveScanResult &aResult); void TryNextJoinerRouter(otError aPrevError); otError Connect(JoinerRouter &aRouter); @@ -166,6 +205,9 @@ private: void LogCertMessage(const char *aText, const Coap::Message &aMessage) const; #endif + Mac::ExtAddress mId; + JoinerDiscerner mDiscerner; + otJoinerState mState; otJoinerCallback mCallback; diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index 8046ea0bd..15dbe9283 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -50,6 +50,88 @@ enum kPskdMaxLength = 32, ///< Maximum PSKd Length. }; +void JoinerDiscerner::GenerateJoinerId(Mac::ExtAddress &aJoinerId) const +{ + aJoinerId.GenerateRandom(); + CopyTo(aJoinerId); + aJoinerId.SetLocal(true); +} + +bool JoinerDiscerner::Matches(const Mac::ExtAddress &aJoinerId) const +{ + uint64_t mask; + + OT_ASSERT(IsValid()); + + mask = GetMask(); + + return (Encoding::BigEndian::ReadUint64(aJoinerId.m8) & mask) == (mValue & mask); +} + +void JoinerDiscerner::CopyTo(Mac::ExtAddress &aExtAddress) const +{ + // Copies the discerner value up to its bit length to `aExtAddress` + // array, assuming big-endian encoding (i.e., the discerner lowest bits + // are copied at end of `aExtAddress.m8[]` array). Any initial/remaining + // bits of `aExtAddress` array remain unchanged. + + uint8_t *cur = &aExtAddress.m8[sizeof(Mac::ExtAddress) - 1]; + uint8_t remaining = mLength; + uint64_t value = mValue; + + OT_ASSERT(IsValid()); + + // Write full bytes + while (remaining >= CHAR_BIT) + { + *cur = static_cast(value & 0xff); + value >>= CHAR_BIT; + cur--; + remaining -= CHAR_BIT; + } + + // Write any remaining bits (not a full byte) + if (remaining != 0) + { + uint8_t mask = static_cast((1U << remaining) - 1); + + // `mask` has it lower (lsb) `remaining` bits as `1` and rest as `0`. + // Example with `remaining = 3` -> (1 << 3) - 1 = 0b1000 - 1 = 0b0111. + + *cur &= ~mask; + *cur |= static_cast(value & mask); + } +} + +bool JoinerDiscerner::operator==(const JoinerDiscerner &aOther) const +{ + uint64_t mask = GetMask(); + + return IsValid() && (mLength == aOther.mLength) && ((mValue & mask) == (aOther.mValue & mask)); +} + +JoinerDiscerner::InfoString JoinerDiscerner::ToString(void) const +{ + InfoString str; + + if (mLength <= sizeof(uint16_t) * CHAR_BIT) + { + IgnoreError(str.Set("0x%04x", static_cast(mValue))); + } + else if (mLength <= sizeof(uint32_t) * CHAR_BIT) + { + IgnoreError(str.Set("0x%08x", static_cast(mValue))); + } + else + { + IgnoreError(str.Set("0x%x-%08x", static_cast(mValue >> 32), static_cast(mValue))); + } + + IgnoreError(str.Append("/len:%d", mLength)); + + return str; +} + void SteeringData::Init(uint8_t aLength) { OT_ASSERT(aLength <= kMaxLength); @@ -67,12 +149,24 @@ void SteeringData::UpdateBloomFilter(const Mac::ExtAddress &aJoinerId) { HashBitIndexes indexes; + CalculateHashBitIndexes(aJoinerId, indexes); + UpdateBloomFilter(indexes); +} + +void SteeringData::UpdateBloomFilter(const JoinerDiscerner &aDiscerner) +{ + HashBitIndexes indexes; + + CalculateHashBitIndexes(aDiscerner, indexes); + UpdateBloomFilter(indexes); +} + +void SteeringData::UpdateBloomFilter(const HashBitIndexes &aIndexes) +{ OT_ASSERT((mLength > 0) && (mLength <= kMaxLength)); - CalculateHashBitIndexes(aJoinerId, indexes); - - SetBit(indexes.mIndex[0] % GetNumBits()); - SetBit(indexes.mIndex[1] % GetNumBits()); + SetBit(aIndexes.mIndex[0] % GetNumBits()); + SetBit(aIndexes.mIndex[1] % GetNumBits()); } bool SteeringData::Contains(const Mac::ExtAddress &aJoinerId) const @@ -84,6 +178,15 @@ bool SteeringData::Contains(const Mac::ExtAddress &aJoinerId) const return Contains(indexes); } +bool SteeringData::Contains(const JoinerDiscerner &aDiscerner) const +{ + HashBitIndexes indexes; + + CalculateHashBitIndexes(aDiscerner, indexes); + + return Contains(indexes); +} + bool SteeringData::Contains(const HashBitIndexes &aIndexes) const { return (mLength > 0) && GetBit(aIndexes.mIndex[0] % GetNumBits()) && GetBit(aIndexes.mIndex[1] % GetNumBits()); @@ -104,6 +207,16 @@ void SteeringData::CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, Has aIndexes.mIndex[1] = ansi.Get(); } +void SteeringData::CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes) +{ + Mac::ExtAddress address; + + address.Clear(); + aDiscerner.CopyTo(address); + + CalculateHashBitIndexes(address, aIndexes); +} + bool SteeringData::DoesAllMatch(uint8_t aMatch) const { bool matches = true; diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp index dfebc2fcd..f38600366 100644 --- a/src/core/meshcop/meshcop.hpp +++ b/src/core/meshcop/meshcop.hpp @@ -40,6 +40,7 @@ #include #include +#include #include "coap/coap.hpp" #include "common/message.hpp" @@ -57,6 +58,119 @@ enum kBorderAgentUdpPort = 49191, ///< UDP port of border agent service. }; +/** + * This type represents a Joiner Discerner. + * + */ +class JoinerDiscerner : public otJoinerDiscerner +{ + friend class SteeringData; + +public: + enum + { + kMaxLength = OT_JOINER_MAX_DISCERNER_LENGTH, ///< Maximum length of a Joiner Discerner in bits. + kInfoStringSize = 45, ///< Size of `InfoString` to use with `ToString() + }; + + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef String InfoString; + + /** + * This method clears the Joiner Discerner. + * + */ + void Clear(void) { mLength = 0; } + + /** + * This method indicates whether the Joiner Discerner is empty (no value set). + * + * @returns TRUE if empty, FALSE otherwise. + * + */ + bool IsEmpty(void) const { return mLength == 0; } + + /** + * This method gets the Joiner Discerner's value. + * + * @returns The Joiner Discerner value. + * + */ + uint64_t GetValue(void) const { return mValue; } + + /** + * This method gets the Joiner Discerner's length (in bits). + * + * @return The Joiner Discerner length. + * + */ + uint8_t GetLength(void) const { return mLength; } + + /** + * This method indicates whether the Joiner Discerner is valid (i.e. it not empty and its length is within + * valid range). + * + * @returns TRUE if Joiner Discerner is valid, FALSE otherwise. + * + */ + bool IsValid(void) const { return (0 < mLength) && (mLength <= kMaxLength); } + + /** + * This method generates a Joiner ID from the Discerner. + * + * @param[out] aJoinerId A reference to `Mac::ExtAddress` to output the generated Joiner ID. + * + */ + void GenerateJoinerId(Mac::ExtAddress &aJoinerId) const; + + /** + * This method indicates whether a given Joiner ID matches the Discerner. + * + * @param[in] aJoiner A Joiner ID to match with the Discerner. + * + * @returns TRUE if the Joiner ID matches the Discerner, FALSE otherwise. + * + */ + bool Matches(const Mac::ExtAddress &aJoinerId) const; + + /** + * This method overloads operator `==` to evaluate whether or not two Joiner Discerner instances are equal. + * + * @param[in] aOther The other Joiner Discerner to compare with. + * + * @retval TRUE If the two are equal. + * @retval FALSE If the two are not equal. + * + */ + bool operator==(const JoinerDiscerner &aOther) const; + + /** + * This method overloads operator `!=` to evaluate whether or not two Joiner Discerner instances are equal. + * + * @param[in] aOther The other Joiner Discerner to compare with. + * + * @retval TRUE If the two are not equal. + * @retval FALSE If the two are equal. + * + */ + bool operator!=(const JoinerDiscerner &aOther) const { return !(*this == aOther); } + + /** + * This method converts the Joiner Discerner to a string. + * + * @returns An `InfoString` representation of Joiner Discerner. + * + */ + InfoString ToString(void) const; + +private: + uint64_t GetMask(void) const { return (static_cast(1ULL) << mLength) - 1; } + void CopyTo(Mac::ExtAddress &aExtAddress) const; +}; + /** * This type represents Steering Data (bloom filter). * @@ -141,6 +255,14 @@ public: */ void UpdateBloomFilter(const Mac::ExtAddress &aJoinerId); + /** + * This method updates the bloom filter adding a given Joiner Discerner. + * + * @param[in] aDiscerner The Joiner Discerner to add to bloom filter. + * + */ + void UpdateBloomFilter(const JoinerDiscerner &aDiscerner); + /** * This method indicates whether the bloom filter is empty (all the bits are cleared). * @@ -160,13 +282,23 @@ public: /** * This method indicates whether the bloom filter contains a given Joiner ID. * - * @param[in] aJoinderId A Joiner ID. + * @param[in] aJoinerId A Joiner ID. * * @returns TRUE if the bloom filter contains @p aJoinerId, FALSE otherwise. * */ bool Contains(const Mac::ExtAddress &aJoinerId) const; + /** + * This method indicates whether the bloom filter contains a given Joiner Discerner. + * + * @param[in] aDiscerner A Joiner Discerner. + * + * @returns TRUE if the bloom filter contains @p aDiscerner, FALSE otherwise. + * + */ + bool Contains(const JoinerDiscerner &aDiscerner) const; + /** * This method indicates whether the bloom filter contains the hash bit indexes (derived from a Joiner ID). * @@ -188,6 +320,17 @@ public: */ static void CalculateHashBitIndexes(const Mac::ExtAddress &aJoinerId, HashBitIndexes &aIndexes); + /** + * This static method calculates the bloom filter hash bit indexes from a given Joiner Discerner. + * + * The first hash bit index is derived using CRC16-CCITT and second one using CRC16-ANSI. + * + * @param[in] aDiscerner The Joiner Discerner to calculate the hash bit indexes. + * @param[out] aIndexes A reference to a `HashBitIndexes` structure to output the calculated index values. + * + */ + static void CalculateHashBitIndexes(const JoinerDiscerner &aDiscerner, HashBitIndexes &aIndexes); + private: enum { @@ -204,6 +347,7 @@ private: void ClearBit(uint8_t aBit) { m8[BitIndex(aBit)] &= ~BitFlag(aBit); } bool DoesAllMatch(uint8_t aMatch) const; + void UpdateBloomFilter(const HashBitIndexes &aIndexes); }; /**