[meshcop] adding otJoinerDiscerner (#5137)

The `otJoinerDiscerner` enables new a mechanism for Thread
commissioning. The traditional Thread commissioning process uses
factory assigned EUI-64 of the device to derive the Joiner ID and
identify/filter a joiner (through steering data bloom filter). The
Joiner Discerner (which is an unsigned value along with a
user-specified bit length up to 64 bits) allows users to have more
control and do not rely on factory-assigned EUI-64.

On joiner side, when a a Joiner Discerner value is provided, the
Joiner code uses the discerner value to derive Joiner ID (appending a
random prefix to extend the value to 64 bits) and bloom filter.

On commissioner side, users can add different joiners providing either
an EUI-64 or an associated Joiner Discerner and the code accordingly
match the Joiner IDs and compute steering data bloom filter.
This commit is contained in:
Abtin Keshavarzian
2020-06-26 16:02:09 -07:00
committed by Jonathan Hui
parent 2ebdf874eb
commit 5210ca3eee
14 changed files with 914 additions and 178 deletions
+62 -8
View File
@@ -37,6 +37,7 @@
#include <openthread/dataset.h>
#include <openthread/ip6.h>
#include <openthread/joiner.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/toolchain.h>
@@ -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.
*
+1 -1
View File
@@ -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
+49 -5
View File
@@ -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);
/**
* @}
+14 -4
View File
@@ -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<Commissioner *>(aContext)->HandleJoinerEvent(aEvent, aJoinerId);
static_cast<Commissioner *>(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");
}
+4 -1
View File
@@ -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,
+3 -3
View File
@@ -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;
+41 -12
View File
@@ -46,33 +46,44 @@ otError otCommissionerStart(otInstance * aInstance,
otCommissionerJoinerCallback aJoinerCallback,
void * aCallbackContext)
{
otError error;
Instance &instance = *static_cast<Instance *>(aInstance);
SuccessOrExit(error =
instance.Get<MeshCoP::Commissioner>().Start(aStateCallback, aJoinerCallback, aCallbackContext));
exit:
return error;
return instance.Get<MeshCoP::Commissioner>().Start(aStateCallback, aJoinerCallback, aCallbackContext);
}
otError otCommissionerStop(otInstance *aInstance)
{
otError error;
Instance &instance = *static_cast<Instance *>(aInstance);
SuccessOrExit(error = instance.Get<MeshCoP::Commissioner>().Stop(/* aResign */ true));
return instance.Get<MeshCoP::Commissioner>().Stop(/* aResign */ true);
}
otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui64, const char *aPskd, uint32_t aTimeout)
{
otError error;
MeshCoP::Commissioner &commissioner = static_cast<Instance *>(aInstance)->Get<MeshCoP::Commissioner>();
if (aEui64 == nullptr)
{
error = commissioner.AddJoinerAny(aPskd, aTimeout);
ExitNow();
}
error = commissioner.AddJoiner(*static_cast<const Mac::ExtAddress *>(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<Instance *>(aInstance);
return instance.Get<MeshCoP::Commissioner>().AddJoiner(static_cast<const Mac::ExtAddress *>(aEui64), aPskd,
aTimeout);
return instance.Get<MeshCoP::Commissioner>().AddJoiner(*static_cast<const MeshCoP::JoinerDiscerner *>(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<Instance *>(aInstance)->Get<MeshCoP::Commissioner>();
if (aEui64 == nullptr)
{
error = commissioner.RemoveJoinerAny(/* aTimeout */ 0);
ExitNow();
}
error = commissioner.RemoveJoiner(*static_cast<const Mac::ExtAddress *>(aEui64), /* aTimeout */ 0);
exit:
return error;
}
otError otCommissionerRemoveJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<MeshCoP::Commissioner>().RemoveJoiner(static_cast<const Mac::ExtAddress *>(aEui64), 0);
return instance.Get<MeshCoP::Commissioner>().RemoveJoiner(
*static_cast<const MeshCoP::JoinerDiscerner *>(aDiscerner), 0);
}
otError otCommissionerSetProvisioningUrl(otInstance *aInstance, const char *aProvisioningUrl)
+27 -2
View File
@@ -71,10 +71,35 @@ otJoinerState otJoinerGetState(otInstance *aInstance)
return instance.Get<MeshCoP::Joiner>().GetState();
}
void otJoinerGetId(otInstance *aInstance, otExtAddress *aJoinerId)
const otExtAddress *otJoinerGetId(otInstance *aInstance)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<MeshCoP::Joiner>().GetJoinerId(*static_cast<Mac::ExtAddress *>(aJoinerId));
return &instance.Get<MeshCoP::Joiner>().GetId();
}
otError otJoinerSetDiscerner(otInstance *aInstance, otJoinerDiscerner *aDiscerner)
{
otError error = OT_ERROR_NONE;
MeshCoP::Joiner &joiner = static_cast<Instance *>(aInstance)->Get<MeshCoP::Joiner>();
if (aDiscerner != NULL)
{
error = joiner.SetDiscerner(*static_cast<const MeshCoP::JoinerDiscerner *>(aDiscerner));
}
else
{
error = joiner.ClearDiscerner();
}
return error;
}
const otJoinerDiscerner *otJoinerGetDiscerner(otInstance *aInstance)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<MeshCoP::Joiner>().GetDiscerner();
}
#endif // OPENTHREAD_CONFIG_JOINER_ENABLE
+249 -109
View File
@@ -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<SteeringData &>(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<SteeringData &>(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<uint16_t>(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<Coap::CoapSecure>().SetPsk(reinterpret_cast<const uint8_t *>(joiner->mPsk),
static_cast<uint8_t>(strlen(joiner->mPsk))));
mJoinerIndex = static_cast<uint8_t>(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<Coap::CoapSecure>())) != nullptr, error = OT_ERROR_NO_BUFS);
@@ -1057,14 +1182,12 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State
SuccessOrExit(error = Get<Coap::CoapSecure>().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
+102 -14
View File
@@ -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
+56 -12
View File
@@ -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<Coap::Coap>().AddResource(mJoinerEntrust);
}
void Joiner::GetJoinerId(Mac::ExtAddress &aJoinerId) const
void Joiner::SetIdFromIeeeEui64(void)
{
Get<Radio>().GetIeeeEui64(aJoinerId);
ComputeJoinerId(aJoinerId, aJoinerId);
Mac::ExtAddress eui64;
Get<Radio>().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<Mle::DiscoverScanner>().Discover(Mac::ChannelMask(0), Get<Mac::Mac>().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<Mac::Mac>().SetExtAddress(joinerId);
Get<Mac::Mac>().SetExtAddress(mId);
Get<Mle::MleRouter>().UpdateLinkLocalAddress();
mJoinerRouterIndex = 0;
+44 -2
View File
@@ -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;
+117 -4
View File
@@ -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<uint8_t>(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<uint8_t>((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<uint8_t>(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<uint16_t>(mValue)));
}
else if (mLength <= sizeof(uint32_t) * CHAR_BIT)
{
IgnoreError(str.Set("0x%08x", static_cast<uint32_t>(mValue)));
}
else
{
IgnoreError(str.Set("0x%x-%08x", static_cast<uint32_t>(mValue >> 32), static_cast<uint32_t>(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;
+145 -1
View File
@@ -40,6 +40,7 @@
#include <limits.h>
#include <openthread/instance.h>
#include <openthread/joiner.h>
#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<kInfoStringSize> 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<uint64_t>(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);
};
/**