[dataset] add ValidateTlvs(), helper methods, and unit test (#10111)

This commit enhances dataset handling:

- Adds `ValidateTlvs()` which parses and validates all known TLVs
  within the `Dataset` and checks for any duplicates.
- Introduces a set of `SetFrom()` methods for constructing a `Dataset`
  from various types of inputs, e.g., other `Dataset`, TLV sequences,
  `DatasetInfo` structures, bytes read from `Message`. Now they
  consistently clear the `Dataset` before setting it.
- Adds `WriteTlvsFrom()` to update a `Dataset` replacing/appending a
  set of TLVs.
- Adds `AppendTlvsFrom()` to append an already encoded sequence of
  TLVs without validating/checking the format.
- Renames `Get/SetSize()` to `Get/SetLength()` for consistency.
- Adds `test_dataset.cpp` unit test for basic `Dataset` validation.
This commit is contained in:
Abtin Keshavarzian
2024-05-02 14:40:11 -07:00
committed by GitHub
parent b8f036a570
commit 485cb18ea4
19 changed files with 562 additions and 194 deletions
+2 -5
View File
@@ -589,7 +589,7 @@ otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameSt
* @param[out] aDataset A pointer to where the dataset will be placed.
*
* @retval OT_ERROR_NONE Successfully set @p aDataset from @p aDatasetTlvs.
* @retval OT_ERROR_INVALID_ARGS @p aDatasetTlvs is invalid.
* @retval OT_ERROR_INVALID_ARGS @p aDatasetTlvs's length is longer than `OT_OPERATIONAL_DATASET_MAX_LENGTH`.
*
*/
otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOperationalDataset *aDataset);
@@ -600,11 +600,8 @@ otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOpera
* @param[in] aDataset An Operational dataset to convert to TLVs.
* @param[out] aDatasetTlvs A pointer to dataset TLVs to return the result.
*
* @retval OT_ERROR_NONE Successfully converted @p aDataset and updated @p aDatasetTlvs.
* @retval OT_ERROR_INVALID_ARGS @p aDataset is invalid, does not contain active or pending timestamps.
*
*/
otError otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs);
void otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs);
/**
* Updates a given Operational Dataset.
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (410)
#define OPENTHREAD_API_VERSION (411)
/**
* @addtogroup api-instance
+2 -2
View File
@@ -612,7 +612,7 @@ template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
otOperationalDataset dataset;
SuccessOrExit(error = otDatasetCreateNewNetwork(GetInstancePtr(), &dataset));
SuccessOrExit(error = otDatasetConvertToTlvs(&dataset, &sDatasetTlvs));
otDatasetConvertToTlvs(&dataset, &sDatasetTlvs);
}
#endif
else if (aArgs[0] == "tlvs")
@@ -1105,7 +1105,7 @@ template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[])
{
otOperationalDataset dataset;
otOperationalDatasetTlvs datasetTlvs;
uint16_t tlvsLength = MeshCoP::Dataset::kMaxSize;
uint16_t tlvsLength = OT_OPERATIONAL_DATASET_MAX_LENGTH;
SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, datasetTlvs.mTlvs));
datasetTlvs.mLength = static_cast<uint8_t>(tlvsLength);
+6 -10
View File
@@ -161,26 +161,22 @@ otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOpera
AssertPointerIsNotNull(aDatasetTlvs);
dataset.SetFrom(*aDatasetTlvs);
VerifyOrExit(dataset.IsValid(), error = kErrorInvalidArgs);
SuccessOrExit(error = dataset.SetFrom(*aDatasetTlvs));
VerifyOrExit(dataset.ValidateTlvs() == kErrorNone, error = kErrorInvalidArgs);
dataset.ConvertTo(AsCoreType(aDataset));
exit:
return error;
}
otError otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs)
void otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs)
{
Error error = kErrorNone;
MeshCoP::Dataset dataset;
AssertPointerIsNotNull(aDatasetTlvs);
SuccessOrExit(error = dataset.SetFrom(AsCoreType(aDataset)));
dataset.SetFrom(AsCoreType(aDataset));
dataset.ConvertTo(*aDatasetTlvs);
exit:
return error;
}
otError otDatasetUpdateTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs)
@@ -190,8 +186,8 @@ otError otDatasetUpdateTlvs(const otOperationalDataset *aDataset, otOperationalD
AssertPointerIsNotNull(aDatasetTlvs);
dataset.SetFrom(*aDatasetTlvs);
SuccessOrExit(error = dataset.SetFrom(AsCoreType(aDataset)));
SuccessOrExit(error = dataset.SetFrom(*aDatasetTlvs));
SuccessOrExit(error = dataset.WriteTlvsFrom(AsCoreType(aDataset)));
dataset.ConvertTo(*aDatasetTlvs);
exit:
+4 -4
View File
@@ -224,7 +224,7 @@ Settings::Key Settings::KeyForDatasetType(MeshCoP::Dataset::Type aType)
Error Settings::SaveOperationalDataset(MeshCoP::Dataset::Type aType, const MeshCoP::Dataset &aDataset)
{
Key key = KeyForDatasetType(aType);
Error error = Get<SettingsDriver>().Set(key, aDataset.GetBytes(), aDataset.GetSize());
Error error = Get<SettingsDriver>().Set(key, aDataset.GetBytes(), aDataset.GetLength());
Log(kActionSave, error, key);
@@ -234,12 +234,12 @@ Error Settings::SaveOperationalDataset(MeshCoP::Dataset::Type aType, const MeshC
Error Settings::ReadOperationalDataset(MeshCoP::Dataset::Type aType, MeshCoP::Dataset &aDataset) const
{
Error error = kErrorNone;
uint16_t length = MeshCoP::Dataset::kMaxSize;
uint16_t length = MeshCoP::Dataset::kMaxLength;
SuccessOrExit(error = Get<SettingsDriver>().Get(KeyForDatasetType(aType), aDataset.GetBytes(), &length));
VerifyOrExit(length <= MeshCoP::Dataset::kMaxSize, error = kErrorNotFound);
VerifyOrExit(length <= MeshCoP::Dataset::kMaxLength, error = kErrorNotFound);
aDataset.SetSize(length);
aDataset.SetLength(static_cast<uint8_t>(length));
exit:
return error;
+168 -110
View File
@@ -157,26 +157,34 @@ exit:
}
Dataset::Dataset(void)
: mUpdateTime(0)
, mLength(0)
: mLength(0)
, mUpdateTime(0)
{
ClearAllBytes(mTlvs);
}
void Dataset::Clear(void) { mLength = 0; }
bool Dataset::IsValid(void) const
Error Dataset::ValidateTlvs(void) const
{
bool rval = true;
const Tlv *end = GetTlvsEnd();
Error error = kErrorParse;
const Tlv *end = GetTlvsEnd();
uint16_t validatedLength;
for (const Tlv *cur = GetTlvsStart(); cur < end; cur = cur->GetNext())
VerifyOrExit(mLength <= kMaxLength);
for (const Tlv *tlv = GetTlvsStart(); tlv < end; tlv = tlv->GetNext())
{
VerifyOrExit(!cur->IsExtended() && (cur + 1) <= end && cur->GetNext() <= end && IsTlvValid(*cur), rval = false);
VerifyOrExit(!tlv->IsExtended() && ((tlv + 1) <= end) && (tlv->GetNext() <= end));
VerifyOrExit(IsTlvValid(*tlv));
// Ensure there are no duplicate TLVs.
validatedLength = static_cast<uint16_t>(reinterpret_cast<const uint8_t *>(tlv) - mTlvs);
VerifyOrExit(Tlv::FindTlv(mTlvs, validatedLength, tlv->GetType()) == nullptr);
}
error = kErrorNone;
exit:
return rval;
return error;
}
bool Dataset::IsTlvValid(const Tlv &aTlv)
@@ -308,110 +316,50 @@ void Dataset::ConvertTo(Tlvs &aTlvs) const
aTlvs.mLength = static_cast<uint8_t>(mLength);
}
void Dataset::Set(Type aType, const Dataset &aDataset)
void Dataset::SetFrom(const Dataset &aDataset)
{
memcpy(mTlvs, aDataset.mTlvs, aDataset.mLength);
mLength = aDataset.mLength;
if (aType == kActive)
{
RemoveTlv(Tlv::kPendingTimestamp);
RemoveTlv(Tlv::kDelayTimer);
}
mLength = aDataset.mLength;
mUpdateTime = aDataset.GetUpdateTime();
}
void Dataset::SetFrom(const Tlvs &aTlvs)
{
mLength = aTlvs.mLength;
memcpy(mTlvs, aTlvs.mTlvs, mLength);
}
Error Dataset::SetFrom(const Tlvs &aTlvs) { return SetFrom(aTlvs.mTlvs, aTlvs.mLength); }
Error Dataset::SetFrom(const Info &aDatasetInfo)
Error Dataset::SetFrom(const uint8_t *aTlvs, uint8_t aLength)
{
Error error = kErrorNone;
if (aDatasetInfo.IsPresent<kActiveTimestamp>())
{
Timestamp activeTimestamp;
VerifyOrExit(aLength <= kMaxLength, error = kErrorInvalidArgs);
aDatasetInfo.Get<kActiveTimestamp>(activeTimestamp);
IgnoreError(Write<ActiveTimestampTlv>(activeTimestamp));
}
if (aDatasetInfo.IsPresent<kPendingTimestamp>())
{
Timestamp pendingTimestamp;
aDatasetInfo.Get<kPendingTimestamp>(pendingTimestamp);
IgnoreError(Write<PendingTimestampTlv>(pendingTimestamp));
}
if (aDatasetInfo.IsPresent<kDelay>())
{
IgnoreError(Write<DelayTimerTlv>(aDatasetInfo.Get<kDelay>()));
}
if (aDatasetInfo.IsPresent<kChannel>())
{
ChannelTlvValue channelValue;
channelValue.SetChannelAndPage(aDatasetInfo.Get<kChannel>());
IgnoreError(Write<ChannelTlv>(channelValue));
}
if (aDatasetInfo.IsPresent<kChannelMask>())
{
ChannelMaskTlv::Value value;
ChannelMaskTlv::PrepareValue(value, aDatasetInfo.Get<kChannelMask>());
IgnoreError(WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
}
if (aDatasetInfo.IsPresent<kExtendedPanId>())
{
IgnoreError(Write<ExtendedPanIdTlv>(aDatasetInfo.Get<kExtendedPanId>()));
}
if (aDatasetInfo.IsPresent<kMeshLocalPrefix>())
{
IgnoreError(Write<MeshLocalPrefixTlv>(aDatasetInfo.Get<kMeshLocalPrefix>()));
}
if (aDatasetInfo.IsPresent<kNetworkKey>())
{
IgnoreError(Write<NetworkKeyTlv>(aDatasetInfo.Get<kNetworkKey>()));
}
if (aDatasetInfo.IsPresent<kNetworkName>())
{
NameData nameData = aDatasetInfo.Get<kNetworkName>().GetAsData();
IgnoreError(WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
}
if (aDatasetInfo.IsPresent<kPanId>())
{
IgnoreError(Write<PanIdTlv>(aDatasetInfo.Get<kPanId>()));
}
if (aDatasetInfo.IsPresent<kPskc>())
{
IgnoreError(Write<PskcTlv>(aDatasetInfo.Get<kPskc>()));
}
if (aDatasetInfo.IsPresent<kSecurityPolicy>())
{
SecurityPolicyTlv tlv;
tlv.Init();
tlv.SetSecurityPolicy(aDatasetInfo.Get<kSecurityPolicy>());
IgnoreError(WriteTlv(tlv));
}
mLength = aLength;
memcpy(mTlvs, aTlvs, mLength);
mUpdateTime = TimerMilli::GetNow();
exit:
return error;
}
void Dataset::SetFrom(const Info &aDatasetInfo)
{
Clear();
IgnoreError(WriteTlvsFrom(aDatasetInfo));
// `mUpdateTime` is already set by `WriteTlvsFrom()`.
}
Error Dataset::SetFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength)
{
Error error = kErrorNone;
VerifyOrExit(aLength <= kMaxLength, error = kErrorInvalidArgs);
SuccessOrExit(error = aMessage.Read(aOffset, mTlvs, aLength));
mLength = static_cast<uint8_t>(aLength);
mUpdateTime = TimerMilli::GetNow();
exit:
return error;
}
@@ -451,19 +399,129 @@ exit:
Error Dataset::WriteTlv(const Tlv &aTlv) { return WriteTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength()); }
Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength)
Error Dataset::WriteTlvsFrom(const Dataset &aDataset)
{
Error error = kErrorParse;
Error error;
VerifyOrExit(aLength <= kMaxSize);
SuccessOrExit(error = aDataset.ValidateTlvs());
SuccessOrExit(aMessage.Read(aOffset, mTlvs, aLength));
mLength = aLength;
for (const Tlv *tlv = aDataset.GetTlvsStart(); tlv < aDataset.GetTlvsEnd(); tlv = tlv->GetNext())
{
SuccessOrExit(error = WriteTlv(*tlv));
}
VerifyOrExit(IsValid(), error = kErrorParse);
exit:
return error;
}
mUpdateTime = TimerMilli::GetNow();
error = kErrorNone;
Error Dataset::WriteTlvsFrom(const uint8_t *aTlvs, uint8_t aLength)
{
Error error;
Dataset dataset;
SuccessOrExit(error = dataset.SetFrom(aTlvs, aLength));
error = WriteTlvsFrom(dataset);
exit:
return error;
}
Error Dataset::WriteTlvsFrom(const Dataset::Info &aDatasetInfo)
{
Error error = kErrorNone;
if (aDatasetInfo.IsPresent<kActiveTimestamp>())
{
Timestamp activeTimestamp;
aDatasetInfo.Get<kActiveTimestamp>(activeTimestamp);
SuccessOrExit(error = Write<ActiveTimestampTlv>(activeTimestamp));
}
if (aDatasetInfo.IsPresent<kPendingTimestamp>())
{
Timestamp pendingTimestamp;
aDatasetInfo.Get<kPendingTimestamp>(pendingTimestamp);
SuccessOrExit(error = Write<PendingTimestampTlv>(pendingTimestamp));
}
if (aDatasetInfo.IsPresent<kDelay>())
{
SuccessOrExit(error = Write<DelayTimerTlv>(aDatasetInfo.Get<kDelay>()));
}
if (aDatasetInfo.IsPresent<kChannel>())
{
ChannelTlvValue channelValue;
channelValue.SetChannelAndPage(aDatasetInfo.Get<kChannel>());
SuccessOrExit(error = Write<ChannelTlv>(channelValue));
}
if (aDatasetInfo.IsPresent<kChannelMask>())
{
ChannelMaskTlv::Value value;
ChannelMaskTlv::PrepareValue(value, aDatasetInfo.Get<kChannelMask>());
SuccessOrExit(error = WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
}
if (aDatasetInfo.IsPresent<kExtendedPanId>())
{
SuccessOrExit(error = Write<ExtendedPanIdTlv>(aDatasetInfo.Get<kExtendedPanId>()));
}
if (aDatasetInfo.IsPresent<kMeshLocalPrefix>())
{
SuccessOrExit(error = Write<MeshLocalPrefixTlv>(aDatasetInfo.Get<kMeshLocalPrefix>()));
}
if (aDatasetInfo.IsPresent<kNetworkKey>())
{
SuccessOrExit(error = Write<NetworkKeyTlv>(aDatasetInfo.Get<kNetworkKey>()));
}
if (aDatasetInfo.IsPresent<kNetworkName>())
{
NameData nameData = aDatasetInfo.Get<kNetworkName>().GetAsData();
SuccessOrExit(error = WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
}
if (aDatasetInfo.IsPresent<kPanId>())
{
SuccessOrExit(error = Write<PanIdTlv>(aDatasetInfo.Get<kPanId>()));
}
if (aDatasetInfo.IsPresent<kPskc>())
{
SuccessOrExit(error = Write<PskcTlv>(aDatasetInfo.Get<kPskc>()));
}
if (aDatasetInfo.IsPresent<kSecurityPolicy>())
{
SecurityPolicyTlv tlv;
tlv.Init();
tlv.SetSecurityPolicy(aDatasetInfo.Get<kSecurityPolicy>());
SuccessOrExit(error = WriteTlv(tlv));
}
exit:
return error;
}
Error Dataset::AppendTlvsFrom(const uint8_t *aTlvs, uint8_t aLength)
{
Error error = kErrorNone;
uint16_t newLength = mLength;
newLength += aLength;
VerifyOrExit(newLength <= kMaxLength, error = kErrorNoBufs);
memcpy(mTlvs + mLength, aTlvs, aLength);
mLength += aLength;
exit:
return error;
@@ -496,7 +554,7 @@ Error Dataset::ApplyConfiguration(Instance &aInstance, bool &aIsNetworkKeyUpdate
KeyManager &keyManager = aInstance.Get<KeyManager>();
Error error = kErrorNone;
VerifyOrExit(IsValid(), error = kErrorParse);
SuccessOrExit(error = ValidateTlvs());
aIsNetworkKeyUpdated = false;
+114 -39
View File
@@ -61,7 +61,7 @@ class Dataset
friend class DatasetLocal;
public:
static constexpr uint8_t kMaxSize = OT_OPERATIONAL_DATASET_MAX_LENGTH; ///< Max size of MeshCoP Dataset (bytes)
static constexpr uint8_t kMaxLength = OT_OPERATIONAL_DATASET_MAX_LENGTH; ///< Max length of Dataset (bytes)
static constexpr uint8_t kMaxValueSize = 16; ///< Max size of a TLV value (bytes)
/**
@@ -242,15 +242,21 @@ public:
* Clears the Dataset.
*
*/
void Clear(void);
void Clear(void) { mLength = 0; }
/**
* Indicates whether or not the dataset appears to be well-formed.
* Parses and validates all TLVs contained within the Dataset.
*
* @returns TRUE if the dataset appears to be well-formed, FALSE otherwise.
* Performs the following checks all TLVs in the Dataset:
* - Ensures correct TLV format and expected minimum length for known TLV types that may appear in a Dataset.
* - Validates TLV value when applicable (e.g., Channel TLV using a supported channel).
* - Ensures no duplicate occurrence of same TLV type.
*
* @retval kErrorNone Successfully validated all the TLVs in the Dataset.
* @retval kErrorParse Dataset TLVs is not well-formed.
*
*/
bool IsValid(void) const;
Error ValidateTlvs(void) const;
/**
* Validates the format and value of a given MeshCoP TLV used in Dataset.
@@ -429,6 +435,66 @@ public:
return WriteTlv(static_cast<Tlv::Type>(UintTlvType::kType), &value, sizeof(value));
}
/**
* Writes TLVs parsed from a given Dataset into this Dataset.
*
* TLVs from @p aDataset are parsed and written in the current Dataset. If the same TLV already exists, it will be
* replaced. Otherwise, the TLV will be appended.
*
* @param[in] aDataset A Dataset.
*
* @retval kErrorNone Successfully merged TLVs from @p Dataset into this Dataset.
* @retval kErrorParse The @p aDataset is not valid.
* @retval kErrorNoBufs Could not add the TLVs due to insufficient buffer space.
*
*/
Error WriteTlvsFrom(const Dataset &aDataset);
/**
* Writes TLVs parsed from a given buffer containing a sequence of TLVs into this Dataset.
*
* TLVs from @p aTlvs buffer are parsed and written in the current Dataset. If the same TLV already exists, it will
* be replaced. Otherwise, the TLV will be appended.
*
* @param[in] aTlvs A pointer to a buffer containing TLVs.
* @param[in] aLength Number of bytes in @p aTlvs buffer.
*
* @retval kErrorNone Successfully merged TLVs from @p Dataset into this Dataset.
* @retval kErrorParse The @p aTlvs is not valid.
* @retval kErrorNoBufs Could not add the TLVs due to insufficient buffer space.
*
*/
Error WriteTlvsFrom(const uint8_t *aTlvs, uint8_t aLength);
/**
* Writes TLVs corresponding to the components in a given `Dataset::Info` into this Dataset.
*
* If the same TLV already exists, it will be replaced. Otherwise the TLV will be appended.
*
* @param[in] aDataseInfo A `Dataset::Info`.
*
* @retval kErrorNone Successfully merged TLVs from @p aDataseInfo into this Dataset.
* @retval kErrorNoBufs Could not add the TLVs due to insufficient buffer space.
*
*/
Error WriteTlvsFrom(const Dataset::Info &aDatasetInfo);
/**
* Appends a given sequence of TLVs to the Dataset.
*
* @note Unlike `WriteTlvsFrom()`, this method does not validate the @p aTlvs to be well-formed or check that there
* are no duplicates. It is up to caller to validate the resulting `Dataset` (e.g., using `ValidateTlvs()`) if
* desired.
*
* @param[in] aTlvs A pointer to a buffer containing TLVs.
* @param[in] aLength Number of bytes in @p aTlvs buffer.
*
* @retval kErrorNone Successfully merged TLVs from @p Dataset into this Dataset.
* @retval kErrorNoBufs Could not append the TLVs due to insufficient buffer space.
*
*/
Error AppendTlvsFrom(const uint8_t *aTlvs, uint8_t aLength);
/**
* Removes a TLV from the Dataset.
*
@@ -472,12 +538,12 @@ public:
void ConvertTo(Tlvs &aTlvs) const;
/**
* Returns the Dataset size in bytes.
* Returns the Dataset length in bytes.
*
* @returns The Dataset size in bytes.
* @returns The Dataset length in bytes.
*
*/
uint16_t GetSize(void) const { return mLength; }
uint8_t GetLength(void) const { return mLength; }
/**
* Sets the Dataset size in bytes.
@@ -485,7 +551,7 @@ public:
* @param[in] aSize The Dataset size in bytes.
*
*/
void SetSize(uint16_t aSize) { mLength = aSize; }
void SetLength(uint8_t aLength) { mLength = aLength; }
/**
* Returns the local time the dataset was last updated.
@@ -496,48 +562,57 @@ public:
TimeMilli GetUpdateTime(void) const { return mUpdateTime; }
/**
* Reads the Dataset from a given message and checks that it is well-formed and valid.
* Sets this Dataset using an existing Dataset.
*
* @param[in] aMessage The message to read from.
* @param[in] aOffset The offset in @p aMessage to start reading the Dataset TLVs.
* @param[in] aLength The dataset length in bytes.
*
* @retval kErrorNone Successfully read and validated the Dataset.
* @retval kErrorParse Could not read or parse the dataset from @p aMessage.
*
*/
Error ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength);
/**
* Sets the Dataset using an existing Dataset.
*
* If this Dataset is an Active Dataset, any Pending Timestamp and Delay Timer TLVs will be omitted in the copy
* from @p aDataset.
*
* @param[in] aType The type of the dataset, active or pending.
* @param[in] aDataset The input Dataset.
*
*/
void Set(Type aType, const Dataset &aDataset);
void SetFrom(const Dataset &aDataset);
/**
* Sets the Dataset from a given structure representation.
*
* @param[in] aDatasetInfo The input Dataset as `Dataset::Info`.
*
* @retval kErrorNone Successfully set the Dataset.
* @retval kErrorInvalidArgs Dataset is missing Active and/or Pending Timestamp.
*
*/
Error SetFrom(const Info &aDatasetInfo);
void SetFrom(const Info &aDatasetInfo);
/**
* Sets the Dataset using @p aDataset.
* Sets the Dataset from a given sequence of TLVs.
*
* @param[in] aDataset The input Dataset as `Tlvs`.
* @param[in] aTlvs The input Dataset as `Tlvs`.
*
* @retval kErrorNone Successfully set the Dataset.
* @retval kErrorInvalidArgs The @p aTlvs is invalid and its length is longer than `kMaxLength`.
*
*/
void SetFrom(const Tlvs &aTlvs);
Error SetFrom(const Tlvs &aTlvs);
/**
* Sets the Dataset from a buffer containing a sequence of TLVs.
*
* @param[in] aTlvs A pointer to a buffer containing TLVs.
* @param[in] aLength Number of bytes in @p aTlvs buffer.
*
* @retval kErrorNone Successfully set the Dataset.
* @retval kErrorInvalidArgs @p aLength is longer than `kMaxLength`.
*
*/
Error SetFrom(const uint8_t *aTlvs, uint8_t aLength);
/**
* Sets the Dataset by reading the TLVs bytes from given message.
*
* @param[in] aMessage The message to read from.
* @param[in] aOffset The offset in @p aMessage to start reading the Dataset TLVs.
* @param[in] aLength The dataset length in bytes.
*
* @retval kErrorNone Successfully set the Dataset.
* @retval kInvalidArgs The @p aLength is longer than `kMaxLength`.
* @retval kErrorParse Could not read or parse the dataset from @p aMessage.
*
*/
Error SetFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength);
/**
* Applies the Active or Pending Dataset to the Thread interface.
@@ -565,7 +640,7 @@ public:
/**
* Converts a Pending Dataset to an Active Dataset.
*
* Removes the Delay Timer and Pending Timestamp TLVs
* Removes the Delay Timer and Pending Timestamp TLVs.
*
*/
void ConvertToActive(void);
@@ -646,9 +721,9 @@ public:
private:
void RemoveTlv(Tlv *aTlv);
uint8_t mTlvs[kMaxSize]; ///< The Dataset buffer
TimeMilli mUpdateTime; ///< Local time last updated
uint16_t mLength; ///< The number of valid bytes in @var mTlvs
uint8_t mTlvs[kMaxLength];
uint8_t mLength;
TimeMilli mUpdateTime; // Local time last updated
};
//---------------------------------------------------------------------------------------------------------------------
+9 -6
View File
@@ -151,7 +151,7 @@ Error DatasetLocal::Save(const Dataset::Info &aDatasetInfo)
Error error;
Dataset dataset;
SuccessOrExit(error = dataset.SetFrom(aDatasetInfo));
dataset.SetFrom(aDatasetInfo);
SuccessOrExit(error = Save(dataset));
exit:
@@ -160,11 +160,14 @@ exit:
Error DatasetLocal::Save(const Dataset::Tlvs &aDatasetTlvs)
{
Error error = kErrorNone;
Dataset dataset;
dataset.SetFrom(aDatasetTlvs);
SuccessOrExit(error = dataset.SetFrom(aDatasetTlvs));
error = Save(dataset);
return Save(dataset);
exit:
return error;
}
Error DatasetLocal::Save(const Dataset &aDataset)
@@ -175,7 +178,7 @@ Error DatasetLocal::Save(const Dataset &aDataset)
DestroySecurelyStoredKeys();
#endif
if (aDataset.GetSize() == 0)
if (aDataset.GetLength() == 0)
{
// do not propagate error back
IgnoreError(Get<Settings>().DeleteOperationalDataset(mType));
@@ -188,7 +191,7 @@ Error DatasetLocal::Save(const Dataset &aDataset)
// Store the network key and PSKC in the secure storage instead of settings.
Dataset dataset;
dataset.Set(GetType(), aDataset);
dataset.SetFrom(aDataset);
MoveKeysToSecureStorage(dataset);
SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(mType, dataset));
#else
@@ -258,7 +261,7 @@ void DatasetLocal::EmplaceSecurelyStoredKeys(Dataset &aDataset) const
{
Dataset dataset;
dataset.Set(GetType(), aDataset);
dataset.SetFrom(aDataset);
MoveKeysToSecureStorage(dataset);
SuccessOrAssert(Get<Settings>().SaveOperationalDataset(mType, dataset));
}
+1
View File
@@ -167,6 +167,7 @@ public:
* @param[in] aDatasetTlvs The Dataset to save as `Dataset::Tlvs`.
*
* @retval kErrorNone Successfully saved the dataset.
* @retval kErrorInvalidArgs The dataset TLVs is invalid, its length is longer than `Dataset::kMaxLength`.
* @retval kErrorNotImplemented The platform does not implement settings functionality.
*
*/
+7 -9
View File
@@ -273,7 +273,7 @@ void DatasetManager::SendSet(void)
VerifyOrExit(message != nullptr, error = kErrorNoBufs);
IgnoreError(Read(dataset));
SuccessOrExit(error = message->AppendBytes(dataset.GetBytes(), dataset.GetSize()));
SuccessOrExit(error = message->AppendBytes(dataset.GetBytes(), dataset.GetLength()));
IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
SuccessOrExit(
@@ -410,14 +410,10 @@ exit:
Error DatasetManager::AppendDatasetToMessage(const Dataset::Info &aDatasetInfo, Message &aMessage) const
{
Error error;
Dataset dataset;
SuccessOrExit(error = dataset.SetFrom(aDatasetInfo));
error = aMessage.AppendBytes(dataset.GetBytes(), dataset.GetSize());
exit:
return error;
dataset.SetFrom(aDatasetInfo);
return aMessage.AppendBytes(dataset.GetBytes(), dataset.GetLength());
}
Error DatasetManager::SendSetRequest(const Dataset::Info &aDatasetInfo,
@@ -605,7 +601,8 @@ Error ActiveDatasetManager::Save(const Timestamp &aTimestamp,
Error error = kErrorNone;
Dataset dataset;
SuccessOrExit(error = dataset.ReadFromMessage(aMessage, aOffset, aLength));
SuccessOrExit(error = dataset.SetFrom(aMessage, aOffset, aLength));
SuccessOrExit(error = dataset.ValidateTlvs());
SuccessOrExit(error = dataset.Write<ActiveTimestampTlv>(aTimestamp));
error = DatasetManager::Save(dataset);
@@ -686,7 +683,8 @@ Error PendingDatasetManager::Save(const Timestamp &aTimestamp,
Error error = kErrorNone;
Dataset dataset;
SuccessOrExit(error = dataset.ReadFromMessage(aMessage, aOffset, aLength));
SuccessOrExit(error = dataset.SetFrom(aMessage, aOffset, aLength));
SuccessOrExit(error = dataset.ValidateTlvs());
SuccessOrExit(dataset.Write<PendingTimestampTlv>(aTimestamp));
SuccessOrExit(error = DatasetManager::Save(dataset));
StartDelayTimer();
+2 -2
View File
@@ -75,7 +75,7 @@ Error DatasetManager::AppendMleDatasetTlv(Message &aMessage) const
dataset.RemoveTlv(IsActiveDataset() ? Tlv::kActiveTimestamp : Tlv::kPendingTimestamp);
return Tlv::AppendTlv(aMessage, mleTlvType, dataset.GetBytes(), static_cast<uint8_t>(dataset.GetSize()));
return Tlv::AppendTlv(aMessage, mleTlvType, dataset.GetBytes(), dataset.GetLength());
}
Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -104,7 +104,7 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo
}
// verify that does not overflow dataset buffer
VerifyOrExit((offset - aMessage.GetOffset()) <= Dataset::kMaxSize);
VerifyOrExit((offset - aMessage.GetOffset()) <= Dataset::kMaxLength);
// verify the request includes a timestamp that is ahead of the locally stored value
SuccessOrExit(Tlv::Find<ActiveTimestampTlv>(aMessage, activeTimestamp));
+1 -1
View File
@@ -117,7 +117,7 @@ void DatasetUpdater::PreparePendingDataset(void)
ExitNow(error = kErrorNone);
}
IgnoreError(dataset.SetFrom(requestedDataset));
SuccessOrExit(dataset.WriteTlvsFrom(requestedDataset));
if (!requestedDataset.IsPresent<Dataset::kDelay>())
{
+2 -1
View File
@@ -465,7 +465,8 @@ Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMess
Dataset::Tlvs datasetTlvs;
Error error;
SuccessOrExit(error = dataset.ReadFromMessage(aIncommingMessage, aOffset, aLength));
SuccessOrExit(error = dataset.SetFrom(aIncommingMessage, aOffset, aLength));
SuccessOrExit(error = dataset.ValidateTlvs());
if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
mDeviceAuthorizationField.mApplicationFlags, &dataset))
+1
View File
@@ -173,6 +173,7 @@ ot_unit_test(child)
ot_unit_test(child_table)
ot_unit_test(cmd_line_parser)
ot_unit_test(data)
ot_unit_test(dataset)
ot_unit_test(dns)
ot_unit_test(dns_client)
ot_unit_test(dso)
+238
View File
@@ -0,0 +1,238 @@
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <openthread/config.h>
#include "test_platform.h"
#include "test_util.hpp"
#include "meshcop/dataset.hpp"
namespace ot {
namespace MeshCoP {
void TestDataset(void)
{
static const uint8_t kTlvBytes[] = {
0x0e, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x35, 0x06, 0x00,
0x04, 0x00, 0x1f, 0xff, 0xe0, 0x02, 0x08, 0x1d, 0xe5, 0xbf, 0xec, 0xd5, 0x16, 0x5b, 0x8f, 0x07, 0x08, 0xfd,
0xe2, 0x1f, 0x0c, 0x8a, 0x13, 0xe8, 0xe7, 0x05, 0x10, 0xea, 0xf9, 0x14, 0x9f, 0xdc, 0x73, 0x78, 0x77, 0x06,
0x98, 0xd5, 0x91, 0x80, 0x22, 0x19, 0x58, 0x03, 0x0f, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x68, 0x72, 0x65, 0x61,
0x64, 0x2d, 0x61, 0x61, 0x63, 0x33, 0x01, 0x02, 0xfa, 0xce, 0x04, 0x10, 0x2e, 0xaa, 0xe2, 0x94, 0x84, 0x38,
0x8e, 0x31, 0x19, 0x58, 0x1a, 0x7b, 0x5a, 0x94, 0x8c, 0x07, 0x0c, 0x04, 0x02, 0xa0, 0xf7, 0xf8,
};
static const otNetworkKey kNetworkKey = {
{0xea, 0xf9, 0x14, 0x9f, 0xdc, 0x73, 0x78, 0x77, 0x06, 0x98, 0xd5, 0x91, 0x80, 0x22, 0x19, 0x58}};
static const otNetworkKey kNewNetworkKey = {
{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}};
static const uint8_t kDuplicateChannels[] = {
0x00, 0x03, 0x00, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x00, 0x1a,
};
static const Tlv::Type kDatasetTlvTypes[] = {
Tlv::kChannel, Tlv::kPanId, Tlv::kExtendedPanId, Tlv::kNetworkName, Tlv::kPskc,
Tlv::kNetworkKey, Tlv::kMeshLocalPrefix, Tlv::kSecurityPolicy, Tlv::kActiveTimestamp,
};
Dataset dataset;
Dataset dataset2;
Dataset::Tlvs datasetTlvs;
Dataset::Info datasetInfo;
uint16_t panId;
NetworkKey networkKey;
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
VerifyOrQuit(dataset.GetLength() == sizeof(kTlvBytes));
SuccessOrQuit(dataset.ValidateTlvs());
for (Tlv::Type tlvType : kDatasetTlvTypes)
{
VerifyOrQuit(dataset.ContainsTlv(tlvType));
}
// Converting to `Dataset::Tlvs`
dataset.ConvertTo(datasetTlvs);
VerifyOrQuit(datasetTlvs.mLength == sizeof(kTlvBytes));
VerifyOrQuit(memcmp(datasetTlvs.mTlvs, kTlvBytes, sizeof(kTlvBytes)) == 0);
// Converting to `Dataset::Info`
dataset.ConvertTo(datasetInfo);
VerifyOrQuit(datasetInfo.mComponents.mIsActiveTimestampPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsNetworkKeyPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsNetworkNamePresent);
VerifyOrQuit(datasetInfo.mComponents.mIsExtendedPanIdPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsMeshLocalPrefixPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsPanIdPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsChannelPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsPskcPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsSecurityPolicyPresent);
VerifyOrQuit(datasetInfo.mComponents.mIsChannelMaskPresent);
VerifyOrQuit(!datasetInfo.mComponents.mIsPendingTimestampPresent);
VerifyOrQuit(!datasetInfo.mComponents.mIsDelayPresent);
VerifyOrQuit(datasetInfo.mPanId == 0xface);
VerifyOrQuit(AsCoreType(&datasetInfo.mNetworkKey) == AsCoreType(&kNetworkKey));
// Finding, reading TLVs
VerifyOrQuit(dataset.Contains<PanIdTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kPanId) != nullptr);
SuccessOrQuit(dataset.Read<PanIdTlv>(panId));
VerifyOrQuit(panId == 0xface);
VerifyOrQuit(dataset.Contains<NetworkKeyTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kNetworkKey) != nullptr);
SuccessOrQuit(dataset.Read<NetworkKeyTlv>(networkKey));
VerifyOrQuit(networkKey == AsCoreType(&kNetworkKey));
// Change PAN ID TLV
SuccessOrQuit(dataset.Write<PanIdTlv>(0xcafe));
SuccessOrQuit(dataset.ValidateTlvs());
VerifyOrQuit(dataset.Contains<PanIdTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kPanId) != nullptr);
SuccessOrQuit(dataset.Read<PanIdTlv>(panId));
VerifyOrQuit(panId == 0xcafe);
for (Tlv::Type tlvType : kDatasetTlvTypes)
{
VerifyOrQuit(dataset.ContainsTlv(tlvType));
}
// Change Network Key TLV
SuccessOrQuit(dataset.Write<NetworkKeyTlv>(AsCoreType(&kNewNetworkKey)));
VerifyOrQuit(dataset.Contains<NetworkKeyTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kNetworkKey) != nullptr);
SuccessOrQuit(dataset.Read<NetworkKeyTlv>(networkKey));
VerifyOrQuit(networkKey == AsCoreType(&kNewNetworkKey));
for (Tlv::Type tlvType : kDatasetTlvTypes)
{
VerifyOrQuit(dataset.ContainsTlv(tlvType));
}
// Remove PAN ID TLV
dataset.RemoveTlv(Tlv::kPanId);
VerifyOrQuit(!dataset.Contains<PanIdTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kPanId) == nullptr);
VerifyOrQuit(dataset.Read<PanIdTlv>(panId) == kErrorNotFound);
SuccessOrQuit(dataset.ValidateTlvs());
// Invalid datasets
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes) - 1));
VerifyOrQuit(dataset.ValidateTlvs() == kErrorParse);
SuccessOrQuit(dataset.SetFrom(kDuplicateChannels, sizeof(kDuplicateChannels)));
VerifyOrQuit(dataset.ValidateTlvs() == kErrorParse);
SuccessOrQuit(dataset.SetFrom(kDuplicateChannels, sizeof(kDuplicateChannels) / 2));
SuccessOrQuit(dataset.ValidateTlvs());
// Combining/Merging TLVs from two Datasets.
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
datasetInfo.Clear();
datasetInfo.mComponents.mIsPanIdPresent = true;
datasetInfo.mComponents.mIsNetworkKeyPresent = true;
datasetInfo.mPanId = 0xcafe;
datasetInfo.mNetworkKey = kNewNetworkKey;
dataset2.SetFrom(datasetInfo);
SuccessOrQuit(dataset2.ValidateTlvs());
SuccessOrQuit(dataset.WriteTlvsFrom(dataset2));
SuccessOrQuit(dataset.ValidateTlvs());
VerifyOrQuit(dataset.Contains<PanIdTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kPanId) != nullptr);
SuccessOrQuit(dataset.Read<PanIdTlv>(panId));
VerifyOrQuit(panId == 0xcafe);
VerifyOrQuit(dataset.Contains<NetworkKeyTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kNetworkKey) != nullptr);
SuccessOrQuit(dataset.Read<NetworkKeyTlv>(networkKey));
VerifyOrQuit(networkKey == AsCoreType(&kNewNetworkKey));
// Combining/Merging TLVs from two Datasets (using `Dataset::Info`).
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
SuccessOrQuit(dataset.WriteTlvsFrom(datasetInfo));
SuccessOrQuit(dataset.ValidateTlvs());
VerifyOrQuit(dataset.Contains<PanIdTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kPanId) != nullptr);
SuccessOrQuit(dataset.Read<PanIdTlv>(panId));
VerifyOrQuit(panId == 0xcafe);
VerifyOrQuit(dataset.Contains<NetworkKeyTlv>());
VerifyOrQuit(dataset.FindTlv(Tlv::kNetworkKey) != nullptr);
SuccessOrQuit(dataset.Read<NetworkKeyTlv>(networkKey));
VerifyOrQuit(networkKey == AsCoreType(&kNewNetworkKey));
// Append TLVs
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
VerifyOrQuit(dataset.GetLength() == sizeof(kTlvBytes));
VerifyOrQuit(memcmp(dataset.GetBytes(), kTlvBytes, sizeof(kTlvBytes)) == 0);
SuccessOrQuit(dataset.AppendTlvsFrom(kTlvBytes, sizeof(kTlvBytes)));
VerifyOrQuit(dataset.GetLength() == 2 * sizeof(kTlvBytes));
VerifyOrQuit(memcmp(dataset.GetBytes(), kTlvBytes, sizeof(kTlvBytes)) == 0);
VerifyOrQuit(memcmp(dataset.GetBytes() + sizeof(kTlvBytes), kTlvBytes, sizeof(kTlvBytes)) == 0);
VerifyOrQuit(dataset.ValidateTlvs() == kErrorParse);
}
} // namespace MeshCoP
} // namespace ot
int main(void)
{
ot::MeshCoP::TestDataset();
printf("All tests passed\n");
return 0;
}
+1 -1
View File
@@ -208,7 +208,7 @@ void InitTest(void)
otOperationalDatasetTlvs datasetTlvs;
SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset));
SuccessOrQuit(otDatasetConvertToTlvs(&dataset, &datasetTlvs));
otDatasetConvertToTlvs(&dataset, &datasetTlvs);
SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
SuccessOrQuit(otIp6SetEnabled(sInstance, true));
+1 -1
View File
@@ -1185,7 +1185,7 @@ void InitTest(bool aEnablBorderRouting = false, bool aAfterReset = false)
otOperationalDatasetTlvs datasetTlvs;
SuccessOrQuit(otDatasetConvertToTlvs(&kDataset, &datasetTlvs));
otDatasetConvertToTlvs(&kDataset, &datasetTlvs);
SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
SuccessOrQuit(otIp6SetEnabled(sInstance, true));
+1 -1
View File
@@ -537,7 +537,7 @@ void InitTest(void)
otOperationalDatasetTlvs datasetTlvs;
SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset));
SuccessOrQuit(otDatasetConvertToTlvs(&dataset, &datasetTlvs));
otDatasetConvertToTlvs(&dataset, &datasetTlvs);
SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
SuccessOrQuit(otIp6SetEnabled(sInstance, true));
+1 -1
View File
@@ -205,7 +205,7 @@ void InitTest(void)
otOperationalDatasetTlvs datasetTlvs;
SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset));
SuccessOrQuit(otDatasetConvertToTlvs(&dataset, &datasetTlvs));
otDatasetConvertToTlvs(&dataset, &datasetTlvs);
SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
SuccessOrQuit(otIp6SetEnabled(sInstance, true));