mirror of
https://github.com/espressif/openthread.git
synced 2026-08-17 16:09:51 +00:00
[dataset] allow setting partially complete active dataset (#3773)
When a partial dataset is stored, the device will attempt to attach to a Thread network using the limited parameters available. If the device successfully attaches, the device will then obtain the complete Active Dataset from its Parent. If the device is router-capable, it will not become a Router/Leader until it has successfully retrieved a complete Active Dataset. Saving a partial dataset supports out-of-band commissioning scenarios.
This commit is contained in:
@@ -306,6 +306,19 @@ OTAPI otError OTCALL otDatasetGetActive(otInstance *aInstance, otOperationalData
|
||||
/**
|
||||
* This function sets the Active Operational Dataset.
|
||||
*
|
||||
* If the dataset does not include an Active Timestamp, the dataset is only partially complete.
|
||||
*
|
||||
* If Thread is enabled on a device that has a partially complete Active Dataset, the device will attempt to attach to
|
||||
* an existing Thread network using any existing information in the dataset. The minimum set of information needed to
|
||||
* attach is the PAN ID and Thread Master Key.
|
||||
*
|
||||
* If channel is not included in the dataset, the device will send MLE Announce messages across different channels to
|
||||
* find neighbors on other channels.
|
||||
*
|
||||
* If the device successfully attaches to a Thread network, the device will then retrieve the full Active Dataset from
|
||||
* its Parent. Note that a router-capable device will not transition to the Router or Leader roles until it has a
|
||||
* complete Active Dataset.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aDataset A pointer to the Active Operational Dataset.
|
||||
*
|
||||
|
||||
@@ -257,34 +257,32 @@ void Dataset::Set(const Dataset &aDataset)
|
||||
|
||||
otError Dataset::Set(const otOperationalDataset &aDataset)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
MeshCoP::ActiveTimestampTlv activeTimestampTlv;
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aDataset.mComponents.mIsActiveTimestampPresent, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
activeTimestampTlv.Init();
|
||||
activeTimestampTlv.SetSeconds(aDataset.mActiveTimestamp);
|
||||
activeTimestampTlv.SetTicks(0);
|
||||
Set(activeTimestampTlv);
|
||||
|
||||
if (mType == Tlv::kPendingTimestamp)
|
||||
if (aDataset.mComponents.mIsActiveTimestampPresent)
|
||||
{
|
||||
MeshCoP::PendingTimestampTlv pendingTimestampTlv;
|
||||
MeshCoP::ActiveTimestampTlv tlv;
|
||||
tlv.Init();
|
||||
tlv.SetSeconds(aDataset.mActiveTimestamp);
|
||||
tlv.SetTicks(0);
|
||||
Set(tlv);
|
||||
}
|
||||
|
||||
VerifyOrExit(aDataset.mComponents.mIsPendingTimestampPresent, error = OT_ERROR_INVALID_ARGS);
|
||||
if (aDataset.mComponents.mIsPendingTimestampPresent)
|
||||
{
|
||||
MeshCoP::PendingTimestampTlv tlv;
|
||||
tlv.Init();
|
||||
tlv.SetSeconds(aDataset.mPendingTimestamp);
|
||||
tlv.SetTicks(0);
|
||||
Set(tlv);
|
||||
}
|
||||
|
||||
pendingTimestampTlv.Init();
|
||||
pendingTimestampTlv.SetSeconds(aDataset.mPendingTimestamp);
|
||||
pendingTimestampTlv.SetTicks(0);
|
||||
Set(pendingTimestampTlv);
|
||||
|
||||
if (aDataset.mComponents.mIsDelayPresent)
|
||||
{
|
||||
MeshCoP::DelayTimerTlv tlv;
|
||||
tlv.Init();
|
||||
tlv.SetDelayTimer(aDataset.mDelay);
|
||||
Set(tlv);
|
||||
}
|
||||
if (aDataset.mComponents.mIsDelayPresent)
|
||||
{
|
||||
MeshCoP::DelayTimerTlv tlv;
|
||||
tlv.Init();
|
||||
tlv.SetDelayTimer(aDataset.mDelay);
|
||||
Set(tlv);
|
||||
}
|
||||
|
||||
if (aDataset.mComponents.mIsChannelPresent)
|
||||
@@ -362,7 +360,6 @@ otError Dataset::Set(const otOperationalDataset &aDataset)
|
||||
|
||||
mUpdateTime = TimerMilli::GetNow();
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,10 @@ DatasetLocal::DatasetLocal(Instance &aInstance, const Tlv::Type aType)
|
||||
|
||||
void DatasetLocal::Clear(void)
|
||||
{
|
||||
Get<Settings>().DeleteOperationalDataset(IsActive());
|
||||
mTimestamp.Init();
|
||||
mTimestampPresent = false;
|
||||
Get<Settings>().DeleteOperationalDataset(IsActive());
|
||||
mSaved = false;
|
||||
}
|
||||
|
||||
otError DatasetLocal::Restore(Dataset &aDataset)
|
||||
@@ -71,9 +72,12 @@ otError DatasetLocal::Restore(Dataset &aDataset)
|
||||
const Timestamp *timestamp;
|
||||
otError error;
|
||||
|
||||
mTimestampPresent = false;
|
||||
|
||||
error = Read(aDataset);
|
||||
SuccessOrExit(error);
|
||||
|
||||
mSaved = true;
|
||||
timestamp = aDataset.GetTimestamp();
|
||||
|
||||
if (timestamp != NULL)
|
||||
@@ -81,10 +85,6 @@ otError DatasetLocal::Restore(Dataset &aDataset)
|
||||
mTimestamp = *timestamp;
|
||||
mTimestampPresent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTimestampPresent = false;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -161,21 +161,22 @@ exit:
|
||||
otError DatasetLocal::Save(const Dataset &aDataset)
|
||||
{
|
||||
const Timestamp *timestamp;
|
||||
otError error;
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
if (aDataset.GetSize() == 0)
|
||||
{
|
||||
error = Get<Settings>().DeleteOperationalDataset(IsActive());
|
||||
// do not propagate error back
|
||||
Get<Settings>().DeleteOperationalDataset(IsActive());
|
||||
mSaved = false;
|
||||
otLogInfoMeshCoP("%s dataset deleted", mType == Tlv::kActiveTimestamp ? "Active" : "Pending");
|
||||
}
|
||||
else
|
||||
{
|
||||
error = Get<Settings>().SaveOperationalDataset(IsActive(), aDataset);
|
||||
SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(IsActive(), aDataset));
|
||||
mSaved = true;
|
||||
otLogInfoMeshCoP("%s dataset set", mType == Tlv::kActiveTimestamp ? "Active" : "Pending");
|
||||
}
|
||||
|
||||
SuccessOrExit(error);
|
||||
|
||||
timestamp = aDataset.GetTimestamp();
|
||||
|
||||
if (timestamp != NULL)
|
||||
|
||||
@@ -71,6 +71,15 @@ public:
|
||||
*/
|
||||
void Clear(void);
|
||||
|
||||
/**
|
||||
* This method indicates whether an Active or Pending Dataset is saved in non-volatile memory.
|
||||
*
|
||||
* @retval TRUE if an Active or Pending Dataset is saved in non-volatile memory.
|
||||
* @retval FALSE if an Active or Pending Dataset is not saved in non-volatile memory.
|
||||
*
|
||||
*/
|
||||
bool IsSaved(void) const { return mSaved; }
|
||||
|
||||
/**
|
||||
* This method restores and retrieves the dataset from non-volatile memory.
|
||||
*
|
||||
@@ -149,7 +158,8 @@ private:
|
||||
Timestamp mTimestamp; ///< Active or Pending Timestamp
|
||||
uint32_t mUpdateTime; ///< Local time last updated
|
||||
Tlv::Type mType; ///< Active or Pending
|
||||
bool mTimestampPresent : 1; ///< Whether or not timestamp is present
|
||||
bool mTimestampPresent : 1; ///< Whether a timestamp is present
|
||||
bool mSaved : 1; ///< Whether a dataset is saved in non-volatile
|
||||
};
|
||||
|
||||
} // namespace MeshCoP
|
||||
|
||||
@@ -92,6 +92,8 @@ otError DatasetManager::Restore(void)
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
mTimestampValid = false;
|
||||
|
||||
SuccessOrExit(error = mLocal.Restore(dataset));
|
||||
|
||||
timestamp = dataset.GetTimestamp();
|
||||
@@ -100,11 +102,11 @@ otError DatasetManager::Restore(void)
|
||||
{
|
||||
mTimestamp = *timestamp;
|
||||
mTimestampValid = true;
|
||||
}
|
||||
|
||||
if (mLocal.GetType() == Tlv::kActiveTimestamp)
|
||||
{
|
||||
dataset.ApplyConfiguration(GetInstance());
|
||||
}
|
||||
if (mLocal.GetType() == Tlv::kActiveTimestamp)
|
||||
{
|
||||
dataset.ApplyConfiguration(GetInstance());
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -703,6 +705,11 @@ ActiveDataset::ActiveDataset(Instance &aInstance)
|
||||
Get<Coap::Coap>().AddResource(mResourceGet);
|
||||
}
|
||||
|
||||
bool ActiveDataset::IsPartiallyComplete(void) const
|
||||
{
|
||||
return mLocal.IsSaved() && !mTimestampValid;
|
||||
}
|
||||
|
||||
otError ActiveDataset::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
@@ -313,6 +313,18 @@ public:
|
||||
*/
|
||||
explicit ActiveDataset(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* This method indicates whether the Active Dataset is partially complete.
|
||||
*
|
||||
* This method is primarily used to determine whether a user has supplied a partial Active Dataset for use
|
||||
* with joining a network.
|
||||
*
|
||||
* @retval TRUE if an Active Dataset is saved but does not include an Active Timestamp.
|
||||
* @retval FALSE if an Active Dataset is not saved or does include an Active Timestamp.
|
||||
*
|
||||
*/
|
||||
bool IsPartiallyComplete(void) const;
|
||||
|
||||
/**
|
||||
* This method clears the Active Operational Dataset.
|
||||
*
|
||||
|
||||
+11
-3
@@ -1713,9 +1713,13 @@ bool Mle::PrepareAnnounceState(void)
|
||||
bool shouldAnnounce = false;
|
||||
Mac::ChannelMask channelMask;
|
||||
|
||||
VerifyOrExit((mRole != OT_DEVICE_ROLE_CHILD) && !IsFullThreadDevice() && (mReattachState == kReattachStop));
|
||||
VerifyOrExit((mRole != OT_DEVICE_ROLE_CHILD) && (mReattachState == kReattachStop) &&
|
||||
(Get<MeshCoP::ActiveDataset>().IsPartiallyComplete() || !IsFullThreadDevice()));
|
||||
|
||||
SuccessOrExit(Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask));
|
||||
if (Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask) != OT_ERROR_NONE)
|
||||
{
|
||||
channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
|
||||
}
|
||||
|
||||
mAnnounceDelay = kAnnounceTimeout / (channelMask.GetNumberOfChannels() + 1);
|
||||
|
||||
@@ -2374,7 +2378,11 @@ otError Mle::SendOrphanAnnounce(void)
|
||||
otError error;
|
||||
Mac::ChannelMask channelMask;
|
||||
|
||||
SuccessOrExit(error = Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask));
|
||||
if (Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask) != OT_ERROR_NONE)
|
||||
{
|
||||
channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
|
||||
}
|
||||
|
||||
SuccessOrExit(error = channelMask.GetNextChannel(mAnnounceChannel));
|
||||
|
||||
SendAnnounce(mAnnounceChannel, true);
|
||||
|
||||
@@ -169,6 +169,7 @@ otError MleRouter::BecomeLeader(void)
|
||||
uint32_t partitionId;
|
||||
uint8_t leaderId;
|
||||
|
||||
VerifyOrExit(!Get<MeshCoP::ActiveDataset>().IsPartiallyComplete(), error = OT_ERROR_INVALID_STATE);
|
||||
VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE);
|
||||
VerifyOrExit(mRole != OT_DEVICE_ROLE_LEADER, error = OT_ERROR_NONE);
|
||||
VerifyOrExit(IsRouterRoleEnabled(), error = OT_ERROR_NOT_CAPABLE);
|
||||
|
||||
Reference in New Issue
Block a user