Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <lib/support/ThreadOperationalDataset.h>
19 :
20 : #include <lib/core/CHIPEncoding.h>
21 :
22 : #include <cassert>
23 : #include <cstring>
24 :
25 : namespace chip {
26 : namespace Thread {
27 :
28 : /**
29 : * The Thread specification defines two TLV element formats: a "base" format and an "extended" format.
30 : * The base format is used for TLV elements with a length of 0-254 bytes and is laid out as follows:
31 : *
32 : * +---------+---------+-------------------------+
33 : * | uint8_t | uint8_t | network byte order data |
34 : * | 1 byte | 1 byte | n byte (0 <= n < 255) |
35 : * +---------+---------+-------------------------+
36 : * | Type | Length | Value |
37 : * +---------+---------+-------------------------+
38 : *
39 : * A length values of 0xff (255) is an "escape" value and indicates that the element uses the "extended"
40 : * format, where a two-byte length field follows the 0xff escape byte.
41 : *
42 : * Only the base format is allowed in Thread Operational Datasets, so only this format is supported here.
43 : */
44 : class ThreadTLV final
45 : {
46 : public:
47 : static constexpr uint8_t kLengthEscape = 0xff;
48 :
49 : enum : uint8_t
50 : {
51 : kChannel = 0,
52 : kPanId = 1,
53 : kExtendedPanId = 2,
54 : kNetworkName = 3,
55 : kPSKc = 4,
56 : kMasterKey = 5,
57 : kMeshLocalPrefix = 7,
58 : kSecurityPolicy = 12,
59 : kActiveTimestamp = 14,
60 : kDelayTimer = 52,
61 : kChannelMask = 53,
62 : };
63 :
64 34 : size_t GetSize() const
65 : {
66 : static_assert(sizeof(*this) == 2, "ThreadTLV header should be 2 bytes (type, length)");
67 34 : return sizeof(*this) + GetLength();
68 : }
69 :
70 279 : uint8_t GetType() const { return mType; }
71 :
72 27 : void SetType(uint8_t aType) { mType = aType; }
73 :
74 501 : uint8_t GetLength() const { return mLength; }
75 :
76 27 : void SetLength(size_t aLength)
77 : {
78 27 : assert(aLength < kLengthEscape);
79 27 : mLength = static_cast<uint8_t>(aLength);
80 27 : }
81 :
82 349 : const uint8_t * GetValue() const
83 : {
84 349 : assert(mLength != kLengthEscape);
85 349 : return reinterpret_cast<const uint8_t *>(this) + sizeof(*this);
86 : }
87 :
88 35 : uint8_t * GetValue() { return const_cast<uint8_t *>(const_cast<const ThreadTLV *>(this)->GetValue()); }
89 :
90 6 : ByteSpan GetValueAsSpan() const { return ByteSpan(GetValue(), GetLength()); }
91 :
92 6 : void Get64(uint64_t & aValue) const
93 : {
94 6 : assert(GetLength() == sizeof(aValue));
95 6 : aValue = Encoding::BigEndian::Get64(GetValue());
96 6 : }
97 :
98 2 : void Get32(uint32_t & aValue) const
99 : {
100 2 : assert(GetLength() == sizeof(aValue));
101 2 : aValue = Encoding::BigEndian::Get32(GetValue());
102 2 : }
103 :
104 5 : void Get16(uint16_t & aValue) const
105 : {
106 5 : assert(GetLength() == sizeof(aValue));
107 5 : aValue = Encoding::BigEndian::Get16(GetValue());
108 5 : }
109 :
110 3 : void Set64(uint64_t aValue)
111 : {
112 3 : assert(GetLength() == sizeof(aValue));
113 3 : Encoding::BigEndian::Put64(GetValue(), aValue);
114 3 : }
115 :
116 0 : void Set32(uint32_t aValue)
117 : {
118 0 : assert(GetLength() == sizeof(aValue));
119 0 : Encoding::BigEndian::Put32(GetValue(), aValue);
120 0 : }
121 :
122 4 : void Set16(uint16_t aValue)
123 : {
124 4 : assert(GetLength() == sizeof(aValue));
125 4 : Encoding::BigEndian::Put16(GetValue(), aValue);
126 4 : }
127 :
128 21 : void SetValue(const void * aValue, size_t aLength)
129 : {
130 21 : assert(GetLength() == aLength);
131 21 : memcpy(GetValue(), aValue, aLength);
132 21 : }
133 :
134 3 : void SetValue(const ByteSpan & aValue) { SetValue(aValue.data(), aValue.size()); }
135 :
136 278 : const ThreadTLV * GetNext() const
137 : {
138 : static_assert(alignof(ThreadTLV) == 1, "Wrong alignment for ThreadTLV header");
139 278 : return reinterpret_cast<const ThreadTLV *>(static_cast<const uint8_t *>(GetValue()) + GetLength());
140 : }
141 :
142 7 : ThreadTLV * GetNext() { return reinterpret_cast<ThreadTLV *>(static_cast<uint8_t *>(GetValue()) + GetLength()); }
143 :
144 : private:
145 : uint8_t mType;
146 : uint8_t mLength;
147 : };
148 :
149 : /// OperationalDatasetView
150 :
151 12 : bool OperationalDatasetView::IsValid(ByteSpan aData)
152 : {
153 12 : VerifyOrReturnValue(aData.size() <= kSizeOperationalDataset, false);
154 :
155 11 : const ThreadTLV * tlv = reinterpret_cast<const ThreadTLV *>(aData.begin());
156 11 : const ThreadTLV * end = reinterpret_cast<const ThreadTLV *>(aData.end());
157 74 : while (tlv != end)
158 : {
159 65 : VerifyOrReturnValue(tlv + 1 <= end, false); // out of bounds
160 63 : VerifyOrReturnValue(tlv->GetLength() != ThreadTLV::kLengthEscape, false); // not allowed in a dataset TLV
161 63 : tlv = tlv->GetNext();
162 : }
163 9 : return true;
164 : }
165 :
166 5 : CHIP_ERROR OperationalDatasetView::Init(ByteSpan aData)
167 : {
168 5 : VerifyOrReturnError(IsValid(aData), CHIP_ERROR_INVALID_ARGUMENT);
169 5 : mData = aData;
170 5 : return CHIP_NO_ERROR;
171 : }
172 :
173 98 : const ThreadTLV * OperationalDatasetView::Locate(uint8_t aType) const
174 : {
175 98 : const ThreadTLV * tlv = reinterpret_cast<const ThreadTLV *>(mData.begin());
176 98 : const ThreadTLV * end = reinterpret_cast<const ThreadTLV *>(mData.end());
177 313 : while (tlv < end)
178 : {
179 279 : if (tlv->GetType() == aType)
180 : {
181 64 : return tlv;
182 : }
183 215 : tlv = tlv->GetNext();
184 : }
185 34 : return nullptr;
186 : }
187 :
188 0 : bool OperationalDatasetView::IsCommissioned() const
189 : {
190 0 : return Has(ThreadTLV::kPanId) && Has(ThreadTLV::kMasterKey) && Has(ThreadTLV::kExtendedPanId) && Has(ThreadTLV::kChannel);
191 : }
192 :
193 9 : CHIP_ERROR OperationalDatasetView::GetActiveTimestamp(uint64_t & aActiveTimestamp) const
194 : {
195 9 : const ThreadTLV * tlv = Locate(ThreadTLV::kActiveTimestamp);
196 9 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
197 8 : VerifyOrReturnError(tlv->GetLength() == sizeof(aActiveTimestamp), CHIP_ERROR_INVALID_TLV_ELEMENT);
198 6 : tlv->Get64(aActiveTimestamp);
199 6 : return CHIP_NO_ERROR;
200 : }
201 :
202 6 : CHIP_ERROR OperationalDatasetView::GetChannel(uint16_t & aChannel) const
203 : {
204 6 : const ThreadTLV * tlv = Locate(ThreadTLV::kChannel);
205 6 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
206 5 : VerifyOrReturnError(tlv->GetLength() == 3, CHIP_ERROR_INVALID_TLV_ELEMENT);
207 3 : const uint8_t * value = tlv->GetValue();
208 3 : VerifyOrReturnError(value[0] == 0, CHIP_ERROR_INVALID_TLV_ELEMENT); // Channel Page must be 0
209 3 : aChannel = Encoding::BigEndian::Get16(value + 1);
210 3 : return CHIP_NO_ERROR;
211 : }
212 :
213 4 : CHIP_ERROR OperationalDatasetView::GetExtendedPanId(uint8_t (&aExtendedPanId)[kSizeExtendedPanId]) const
214 : {
215 4 : ByteSpan extPanIdSpan;
216 4 : ReturnErrorOnFailure(GetExtendedPanIdAsByteSpan(extPanIdSpan));
217 3 : memcpy(aExtendedPanId, extPanIdSpan.data(), extPanIdSpan.size());
218 3 : return CHIP_NO_ERROR;
219 : }
220 :
221 0 : CHIP_ERROR OperationalDatasetView::GetExtendedPanId(uint64_t & extendedPanId) const
222 : {
223 0 : ByteSpan extPanIdSpan;
224 0 : ReturnErrorOnFailure(GetExtendedPanIdAsByteSpan(extPanIdSpan));
225 0 : VerifyOrDie(extPanIdSpan.size() == sizeof(extendedPanId));
226 0 : extendedPanId = Encoding::BigEndian::Get64(extPanIdSpan.data());
227 0 : return CHIP_NO_ERROR;
228 : }
229 :
230 7 : CHIP_ERROR OperationalDatasetView::GetExtendedPanIdAsByteSpan(ByteSpan & span) const
231 : {
232 7 : const ThreadTLV * tlv = Locate(ThreadTLV::kExtendedPanId);
233 7 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
234 6 : VerifyOrReturnError(tlv->GetLength() == kSizeExtendedPanId, CHIP_ERROR_INVALID_TLV_ELEMENT);
235 4 : span = tlv->GetValueAsSpan();
236 4 : return CHIP_NO_ERROR;
237 : }
238 :
239 6 : CHIP_ERROR OperationalDatasetView::GetMeshLocalPrefix(uint8_t (&aMeshLocalPrefix)[kSizeMeshLocalPrefix]) const
240 : {
241 6 : const ThreadTLV * tlv = Locate(ThreadTLV::kMeshLocalPrefix);
242 6 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
243 5 : VerifyOrReturnError(tlv->GetLength() == sizeof(aMeshLocalPrefix), CHIP_ERROR_INVALID_TLV_ELEMENT);
244 3 : memcpy(aMeshLocalPrefix, tlv->GetValue(), sizeof(aMeshLocalPrefix));
245 3 : return CHIP_NO_ERROR;
246 : }
247 :
248 7 : CHIP_ERROR OperationalDatasetView::GetMasterKey(uint8_t (&aMasterKey)[kSizeMasterKey]) const
249 : {
250 7 : const ThreadTLV * tlv = Locate(ThreadTLV::kMasterKey);
251 7 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
252 5 : VerifyOrReturnError(tlv->GetLength() == sizeof(aMasterKey), CHIP_ERROR_INVALID_TLV_ELEMENT);
253 3 : memcpy(aMasterKey, tlv->GetValue(), sizeof(aMasterKey));
254 3 : return CHIP_NO_ERROR;
255 : }
256 :
257 8 : CHIP_ERROR OperationalDatasetView::GetNetworkName(char (&aNetworkName)[kSizeNetworkName + 1]) const
258 : {
259 8 : const ThreadTLV * tlv = Locate(ThreadTLV::kNetworkName);
260 8 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
261 7 : VerifyOrReturnError(tlv->GetLength() <= kSizeNetworkName, CHIP_ERROR_INVALID_TLV_ELEMENT);
262 5 : memcpy(aNetworkName, tlv->GetValue(), tlv->GetLength());
263 5 : aNetworkName[tlv->GetLength()] = '\0';
264 5 : return CHIP_NO_ERROR;
265 : }
266 :
267 8 : CHIP_ERROR OperationalDatasetView::GetPanId(uint16_t & aPanId) const
268 : {
269 8 : const ThreadTLV * tlv = Locate(ThreadTLV::kPanId);
270 8 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
271 7 : VerifyOrReturnError(tlv->GetLength() == sizeof(aPanId), CHIP_ERROR_INVALID_TLV_ELEMENT);
272 5 : tlv->Get16(aPanId);
273 5 : return CHIP_NO_ERROR;
274 : }
275 :
276 7 : CHIP_ERROR OperationalDatasetView::GetPSKc(uint8_t (&aPSKc)[kSizePSKc]) const
277 : {
278 7 : const ThreadTLV * tlv = Locate(ThreadTLV::kPSKc);
279 7 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
280 5 : VerifyOrReturnError(tlv->GetLength() == sizeof(aPSKc), CHIP_ERROR_INVALID_TLV_ELEMENT);
281 3 : memcpy(aPSKc, tlv->GetValue(), sizeof(aPSKc));
282 3 : return CHIP_NO_ERROR;
283 : }
284 :
285 4 : CHIP_ERROR OperationalDatasetView::GetChannelMask(ByteSpan & aChannelMask) const
286 : {
287 4 : const ThreadTLV * tlv = Locate(ThreadTLV::kChannelMask);
288 4 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
289 4 : VerifyOrReturnError(tlv->GetLength() > 0, CHIP_ERROR_INVALID_TLV_ELEMENT);
290 2 : aChannelMask = tlv->GetValueAsSpan();
291 2 : return CHIP_NO_ERROR;
292 : }
293 :
294 4 : CHIP_ERROR OperationalDatasetView::GetSecurityPolicy(uint32_t & aSecurityPolicy) const
295 : {
296 4 : const ThreadTLV * tlv = Locate(ThreadTLV::kSecurityPolicy);
297 4 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
298 4 : VerifyOrReturnError(tlv->GetLength() == sizeof(aSecurityPolicy), CHIP_ERROR_INVALID_TLV_ELEMENT);
299 2 : tlv->Get32(aSecurityPolicy);
300 2 : return CHIP_NO_ERROR;
301 : }
302 :
303 0 : CHIP_ERROR OperationalDatasetView::GetDelayTimer(uint32_t & aDelayMillis) const
304 : {
305 0 : const ThreadTLV * tlv = Locate(ThreadTLV::kDelayTimer);
306 0 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND);
307 0 : VerifyOrReturnError(tlv->GetLength() == sizeof(aDelayMillis), CHIP_ERROR_INVALID_TLV_ELEMENT);
308 0 : tlv->Get32(aDelayMillis);
309 0 : return CHIP_NO_ERROR;
310 : }
311 :
312 : /// OperationalDataset
313 :
314 : inline constexpr size_t kMaxDatasetElementLength = kSizeOperationalDataset - sizeof(ThreadTLV);
315 :
316 7 : CHIP_ERROR OperationalDataset::Init(ByteSpan aData)
317 : {
318 7 : VerifyOrReturnError(IsValid(aData), CHIP_ERROR_INVALID_ARGUMENT);
319 : // Use memmove because aData could be a sub-span of AsByteSpan()
320 4 : memmove(mBuffer, aData.data(), aData.size());
321 4 : mData = ByteSpan(mBuffer, aData.size());
322 4 : return CHIP_NO_ERROR;
323 : }
324 :
325 32 : void OperationalDataset::CopyDataIfNecessary()
326 : {
327 : // It's possible that mData points into an external buffer if someone has
328 : // called OperationalDatasetView::Init() instead of our copying version.
329 32 : if (mData.data() != mBuffer)
330 : {
331 1 : CopyData();
332 : }
333 32 : }
334 :
335 4 : void OperationalDataset::CopyData()
336 : {
337 4 : memmove(mBuffer, mData.data(), mData.size());
338 4 : mData = ByteSpan(mBuffer, mData.size());
339 4 : }
340 :
341 7 : void OperationalDataset::Remove(ThreadTLV * tlv)
342 : {
343 7 : size_t size = tlv->GetSize();
344 7 : ThreadTLV * next = tlv->GetNext();
345 7 : memmove(tlv, next, static_cast<size_t>(mData.end() - reinterpret_cast<uint8_t *>(next)));
346 7 : mData = ByteSpan(mData.data(), mData.size() - size);
347 7 : }
348 :
349 2 : void OperationalDataset::Remove(uint8_t aType)
350 : {
351 2 : CopyDataIfNecessary();
352 2 : ThreadTLV * tlv = const_cast<ThreadTLV *>(Locate(aType));
353 2 : if (tlv != nullptr)
354 : {
355 2 : Remove(tlv);
356 : }
357 2 : }
358 :
359 : // Inserts a TLV of the specified type and length into the dataset, replacing an existing TLV
360 : // of the same type if one exists. Returns nullptr if there is not enough space.
361 30 : ThreadTLV * OperationalDataset::InsertOrReplace(uint8_t aType, size_t aValueSize)
362 : {
363 30 : assert(aValueSize <= kMaxDatasetElementLength); // callers check this or a tighter limit
364 30 : CopyDataIfNecessary();
365 30 : ThreadTLV * tlv = const_cast<ThreadTLV *>(Locate(aType));
366 30 : if (tlv != nullptr)
367 : {
368 6 : size_t tlvLength = tlv->GetLength();
369 6 : VerifyOrReturnValue(aValueSize != tlvLength, tlv); // re-use in place if same size
370 5 : VerifyOrReturnValue(aValueSize < tlvLength || mData.size() + aValueSize - tlvLength <= sizeof(mBuffer), nullptr);
371 5 : Remove(tlv); // we could grow or shrink in place instead, but this is simpler
372 : }
373 : else
374 : {
375 24 : VerifyOrReturnValue(mData.size() + sizeof(ThreadTLV) + aValueSize <= sizeof(mBuffer), nullptr);
376 : }
377 :
378 27 : tlv = reinterpret_cast<ThreadTLV *>(mBuffer + mData.size());
379 27 : tlv->SetType(aType);
380 27 : tlv->SetLength(static_cast<uint8_t>(aValueSize));
381 27 : mData = ByteSpan(mBuffer, mData.size() + tlv->GetSize());
382 27 : return tlv;
383 : }
384 :
385 3 : CHIP_ERROR OperationalDataset::SetActiveTimestamp(uint64_t aActiveTimestamp)
386 : {
387 : static_assert(sizeof(aActiveTimestamp) <= kMaxDatasetElementLength);
388 3 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kActiveTimestamp, sizeof(aActiveTimestamp));
389 3 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
390 3 : tlv->Set64(aActiveTimestamp);
391 3 : return CHIP_NO_ERROR;
392 : }
393 :
394 2 : CHIP_ERROR OperationalDataset::SetChannel(uint16_t aChannel)
395 : {
396 2 : uint8_t value[3] = { 0 }; // Channel Page is always 0
397 2 : Encoding::BigEndian::Put16(value + 1, aChannel);
398 : static_assert(sizeof(value) <= kMaxDatasetElementLength);
399 2 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kChannel, sizeof(value));
400 2 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
401 2 : tlv->SetValue(value, sizeof(value));
402 2 : return CHIP_NO_ERROR;
403 : }
404 :
405 2 : CHIP_ERROR OperationalDataset::SetExtendedPanId(const uint8_t (&aExtendedPanId)[kSizeExtendedPanId])
406 : {
407 : static_assert(sizeof(aExtendedPanId) <= kMaxDatasetElementLength);
408 2 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kExtendedPanId, sizeof(aExtendedPanId));
409 2 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
410 2 : tlv->SetValue(aExtendedPanId, sizeof(aExtendedPanId));
411 2 : return CHIP_NO_ERROR;
412 : }
413 :
414 3 : CHIP_ERROR OperationalDataset::SetMasterKey(const uint8_t (&aMasterKey)[kSizeMasterKey])
415 : {
416 : static_assert(sizeof(aMasterKey) <= kMaxDatasetElementLength);
417 3 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kMasterKey, sizeof(aMasterKey));
418 3 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
419 3 : tlv->SetValue(aMasterKey, sizeof(aMasterKey));
420 3 : return CHIP_NO_ERROR;
421 : }
422 :
423 1 : void OperationalDataset::UnsetMasterKey()
424 : {
425 1 : Remove(ThreadTLV::kMasterKey);
426 1 : }
427 :
428 2 : CHIP_ERROR OperationalDataset::SetMeshLocalPrefix(const uint8_t (&aMeshLocalPrefix)[kSizeMeshLocalPrefix])
429 : {
430 : static_assert(sizeof(aMeshLocalPrefix) <= kMaxDatasetElementLength);
431 2 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kMeshLocalPrefix, sizeof(aMeshLocalPrefix));
432 2 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
433 2 : tlv->SetValue(aMeshLocalPrefix, sizeof(aMeshLocalPrefix));
434 2 : return CHIP_NO_ERROR;
435 : }
436 :
437 9 : CHIP_ERROR OperationalDataset::SetNetworkName(const char * aNetworkName)
438 : {
439 9 : VerifyOrReturnError(aNetworkName != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
440 8 : size_t len = strlen(aNetworkName);
441 8 : VerifyOrReturnError(0 < len && len <= kSizeNetworkName, CHIP_ERROR_INVALID_STRING_LENGTH);
442 :
443 : static_assert(kSizeNetworkName <= kMaxDatasetElementLength);
444 6 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kNetworkName, len);
445 6 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
446 6 : tlv->SetValue(aNetworkName, len);
447 6 : return CHIP_NO_ERROR;
448 : }
449 :
450 6 : CHIP_ERROR OperationalDataset::SetPanId(uint16_t aPanId)
451 : {
452 : static_assert(sizeof(aPanId) <= kMaxDatasetElementLength);
453 6 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kPanId, sizeof(aPanId));
454 6 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
455 4 : tlv->Set16(aPanId);
456 4 : return CHIP_NO_ERROR;
457 : }
458 :
459 3 : CHIP_ERROR OperationalDataset::SetPSKc(const uint8_t (&aPSKc)[kSizePSKc])
460 : {
461 : static_assert(sizeof(aPSKc) <= kMaxDatasetElementLength);
462 3 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kPSKc, sizeof(aPSKc));
463 3 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
464 3 : tlv->SetValue(aPSKc, sizeof(aPSKc));
465 3 : return CHIP_NO_ERROR;
466 : }
467 :
468 1 : void OperationalDataset::UnsetPSKc()
469 : {
470 1 : Remove(ThreadTLV::kPSKc);
471 1 : }
472 :
473 3 : CHIP_ERROR OperationalDataset::SetChannelMask(ByteSpan aChannelMask)
474 : {
475 3 : VerifyOrReturnError(0 < aChannelMask.size() && aChannelMask.size() <= kMaxDatasetElementLength, CHIP_ERROR_INVALID_ARGUMENT);
476 3 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kChannelMask, aChannelMask.size());
477 3 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
478 3 : tlv->SetValue(aChannelMask);
479 3 : return CHIP_NO_ERROR;
480 : }
481 :
482 0 : CHIP_ERROR OperationalDataset::SetSecurityPolicy(uint32_t aSecurityPolicy)
483 : {
484 : static_assert(sizeof(aSecurityPolicy) <= kMaxDatasetElementLength);
485 0 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kSecurityPolicy, sizeof(aSecurityPolicy));
486 0 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
487 0 : tlv->Set32(aSecurityPolicy);
488 0 : return CHIP_NO_ERROR;
489 : }
490 :
491 0 : CHIP_ERROR OperationalDataset::SetDelayTimer(uint32_t aDelayMillis)
492 : {
493 : static_assert(sizeof(aDelayMillis) <= kMaxDatasetElementLength);
494 0 : ThreadTLV * tlv = InsertOrReplace(ThreadTLV::kDelayTimer, sizeof(aDelayMillis));
495 0 : VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY);
496 0 : tlv->Set32(aDelayMillis);
497 0 : return CHIP_NO_ERROR;
498 : }
499 :
500 : } // namespace Thread
501 : } // namespace chip
|