1 /*
2 * Copyright (c) 2016, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file implements MeshCoP Datasets manager to process commands.
32 *
33 */
34
35 #include "meshcop/dataset_manager.hpp"
36
37 #if OPENTHREAD_FTD
38
39 #include <stdio.h>
40
41 #include <openthread/platform/radio.h>
42
43 #include "coap/coap_message.hpp"
44 #include "common/as_core_type.hpp"
45 #include "common/code_utils.hpp"
46 #include "common/debug.hpp"
47 #include "common/instance.hpp"
48 #include "common/locator_getters.hpp"
49 #include "common/log.hpp"
50 #include "common/random.hpp"
51 #include "common/timer.hpp"
52 #include "meshcop/dataset.hpp"
53 #include "meshcop/meshcop.hpp"
54 #include "meshcop/meshcop_leader.hpp"
55 #include "meshcop/meshcop_tlvs.hpp"
56 #include "thread/thread_netif.hpp"
57 #include "thread/thread_tlvs.hpp"
58 #include "thread/uri_paths.hpp"
59
60 namespace ot {
61 namespace MeshCoP {
62
63 RegisterLogModule("DatasetManager");
64
AppendMleDatasetTlv(Message & aMessage) const65 Error DatasetManager::AppendMleDatasetTlv(Message &aMessage) const
66 {
67 Dataset dataset;
68
69 IgnoreError(Read(dataset));
70
71 return dataset.AppendMleDatasetTlv(GetType(), aMessage);
72 }
73
HandleSet(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)74 Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
75 {
76 Tlv tlv;
77 uint16_t offset = aMessage.GetOffset();
78 bool isUpdateFromCommissioner = false;
79 bool doesAffectConnectivity = false;
80 bool doesAffectNetworkKey = false;
81 bool hasNetworkKey = false;
82 StateTlv::State state = StateTlv::kReject;
83 Dataset dataset;
84 Timestamp activeTimestamp;
85 ChannelTlv channel;
86 uint16_t sessionId;
87 Ip6::NetworkPrefix meshLocalPrefix;
88 NetworkKey networkKey;
89 uint16_t panId;
90
91 VerifyOrExit(Get<Mle::MleRouter>().IsLeader());
92
93 // verify that TLV data size is less than maximum TLV value size
94 while (offset < aMessage.GetLength())
95 {
96 SuccessOrExit(aMessage.Read(offset, tlv));
97 VerifyOrExit(tlv.GetLength() <= Dataset::kMaxValueSize);
98 offset += sizeof(tlv) + tlv.GetLength();
99 }
100
101 // verify that does not overflow dataset buffer
102 VerifyOrExit((offset - aMessage.GetOffset()) <= Dataset::kMaxSize);
103
104 // verify the request includes a timestamp that is ahead of the locally stored value
105 SuccessOrExit(Tlv::Find<ActiveTimestampTlv>(aMessage, activeTimestamp));
106
107 if (GetType() == Dataset::kPending)
108 {
109 Timestamp pendingTimestamp;
110
111 SuccessOrExit(Tlv::Find<PendingTimestampTlv>(aMessage, pendingTimestamp));
112 VerifyOrExit(Timestamp::Compare(&pendingTimestamp, mLocal.GetTimestamp()) > 0);
113 }
114 else
115 {
116 VerifyOrExit(Timestamp::Compare(&activeTimestamp, mLocal.GetTimestamp()) > 0);
117 }
118
119 // check channel
120 if (Tlv::FindTlv(aMessage, channel) == kErrorNone)
121 {
122 VerifyOrExit(channel.IsValid());
123
124 if (channel.GetChannel() != Get<Mac::Mac>().GetPanChannel())
125 {
126 doesAffectConnectivity = true;
127 }
128 }
129
130 // check PAN ID
131 if (Tlv::Find<PanIdTlv>(aMessage, panId) == kErrorNone && panId != Get<Mac::Mac>().GetPanId())
132 {
133 doesAffectConnectivity = true;
134 }
135
136 // check mesh local prefix
137 if (Tlv::Find<MeshLocalPrefixTlv>(aMessage, meshLocalPrefix) == kErrorNone &&
138 meshLocalPrefix != Get<Mle::MleRouter>().GetMeshLocalPrefix())
139 {
140 doesAffectConnectivity = true;
141 }
142
143 // check network key
144 if (Tlv::Find<NetworkKeyTlv>(aMessage, networkKey) == kErrorNone)
145 {
146 NetworkKey localNetworkKey;
147
148 hasNetworkKey = true;
149 Get<KeyManager>().GetNetworkKey(localNetworkKey);
150
151 if (networkKey != localNetworkKey)
152 {
153 doesAffectConnectivity = true;
154 doesAffectNetworkKey = true;
155 }
156 }
157
158 // check active timestamp rollback
159 if (GetType() == Dataset::kPending && (!hasNetworkKey || !doesAffectNetworkKey))
160 {
161 // no change to network key, active timestamp must be ahead
162 const Timestamp *localActiveTimestamp = Get<ActiveDatasetManager>().GetTimestamp();
163
164 VerifyOrExit(Timestamp::Compare(&activeTimestamp, localActiveTimestamp) > 0);
165 }
166
167 // check commissioner session id
168 if (Tlv::Find<CommissionerSessionIdTlv>(aMessage, sessionId) == kErrorNone)
169 {
170 const CommissionerSessionIdTlv *localId;
171
172 isUpdateFromCommissioner = true;
173
174 localId = As<CommissionerSessionIdTlv>(
175 Get<NetworkData::Leader>().GetCommissioningDataSubTlv(Tlv::kCommissionerSessionId));
176
177 VerifyOrExit(localId != nullptr && localId->GetCommissionerSessionId() == sessionId);
178 }
179
180 // verify an MGMT_ACTIVE_SET.req from a Commissioner does not affect connectivity
181 VerifyOrExit(!isUpdateFromCommissioner || GetType() == Dataset::kPending || !doesAffectConnectivity);
182
183 if (isUpdateFromCommissioner)
184 {
185 // Thread specification allows partial dataset changes for MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req
186 // from Commissioner based on existing active dataset.
187 IgnoreError(Get<ActiveDatasetManager>().Read(dataset));
188 }
189
190 if (GetType() == Dataset::kPending || !doesAffectConnectivity)
191 {
192 offset = aMessage.GetOffset();
193
194 while (offset < aMessage.GetLength())
195 {
196 DatasetTlv datasetTlv;
197
198 SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
199
200 switch (datasetTlv.GetType())
201 {
202 case Tlv::kCommissionerSessionId:
203 // do not store Commissioner Session ID TLV
204 break;
205
206 case Tlv::kDelayTimer:
207 {
208 DelayTimerTlv &delayTimerTlv = As<DelayTimerTlv>(datasetTlv);
209
210 if (doesAffectNetworkKey && delayTimerTlv.GetDelayTimer() < DelayTimerTlv::kDelayTimerDefault)
211 {
212 delayTimerTlv.SetDelayTimer(DelayTimerTlv::kDelayTimerDefault);
213 }
214 else if (delayTimerTlv.GetDelayTimer() < Get<Leader>().GetDelayTimerMinimal())
215 {
216 delayTimerTlv.SetDelayTimer(Get<Leader>().GetDelayTimerMinimal());
217 }
218 }
219
220 OT_FALL_THROUGH;
221
222 default:
223 SuccessOrExit(dataset.SetTlv(datasetTlv));
224 break;
225 }
226
227 offset += static_cast<uint16_t>(datasetTlv.GetSize());
228 }
229
230 SuccessOrExit(Save(dataset));
231 Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
232 }
233 else
234 {
235 Get<PendingDatasetManager>().ApplyActiveDataset(activeTimestamp, aMessage);
236 }
237
238 state = StateTlv::kAccept;
239
240 // notify commissioner if update is from thread device
241 if (!isUpdateFromCommissioner)
242 {
243 const CommissionerSessionIdTlv *localSessionId;
244 Ip6::Address destination;
245
246 localSessionId = As<CommissionerSessionIdTlv>(
247 Get<NetworkData::Leader>().GetCommissioningDataSubTlv(Tlv::kCommissionerSessionId));
248 VerifyOrExit(localSessionId != nullptr);
249
250 SuccessOrExit(
251 Get<Mle::MleRouter>().GetCommissionerAloc(destination, localSessionId->GetCommissionerSessionId()));
252
253 Get<Leader>().SendDatasetChanged(destination);
254 }
255
256 exit:
257
258 if (Get<Mle::MleRouter>().IsLeader())
259 {
260 SendSetResponse(aMessage, aMessageInfo, state);
261 }
262
263 return (state == StateTlv::kAccept) ? kErrorNone : kErrorDrop;
264 }
265
SendSetResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,StateTlv::State aState)266 void DatasetManager::SendSetResponse(const Coap::Message &aRequest,
267 const Ip6::MessageInfo &aMessageInfo,
268 StateTlv::State aState)
269 {
270 Error error = kErrorNone;
271 Coap::Message *message;
272
273 message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
274 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
275
276 SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
277
278 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
279
280 LogInfo("sent dataset set response");
281
282 exit:
283 FreeMessageOnError(message, error);
284 }
285
ReadFromMessage(const Message & aMessage,uint16_t aOffset)286 Error DatasetManager::DatasetTlv::ReadFromMessage(const Message &aMessage, uint16_t aOffset)
287 {
288 Error error = kErrorNone;
289
290 SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(Tlv)));
291 VerifyOrExit(GetLength() <= Dataset::kMaxValueSize, error = kErrorParse);
292 SuccessOrExit(error = aMessage.Read(aOffset + sizeof(Tlv), mValue, GetLength()));
293 VerifyOrExit(Tlv::IsValid(*this), error = kErrorParse);
294
295 exit:
296 return error;
297 }
298
299 #if OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
GenerateLocal(void)300 Error ActiveDatasetManager::GenerateLocal(void)
301 {
302 Error error = kErrorNone;
303 Dataset dataset;
304
305 VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = kErrorInvalidState);
306 VerifyOrExit(!mLocal.IsTimestampPresent(), error = kErrorAlready);
307
308 IgnoreError(Read(dataset));
309
310 if (dataset.GetTlv<ActiveTimestampTlv>() == nullptr)
311 {
312 Timestamp timestamp;
313
314 timestamp.Clear();
315 IgnoreError(dataset.SetTlv(Tlv::kActiveTimestamp, timestamp));
316 }
317
318 if (dataset.GetTlv<ChannelTlv>() == nullptr)
319 {
320 ChannelTlv tlv;
321 tlv.Init();
322 tlv.SetChannel(Get<Mac::Mac>().GetPanChannel());
323 IgnoreError(dataset.SetTlv(tlv));
324 }
325
326 if (dataset.GetTlv<ChannelMaskTlv>() == nullptr)
327 {
328 ChannelMaskTlv tlv;
329 tlv.Init();
330 tlv.SetChannelMask(Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
331 IgnoreError(dataset.SetTlv(tlv));
332 }
333
334 if (dataset.GetTlv<ExtendedPanIdTlv>() == nullptr)
335 {
336 IgnoreError(dataset.SetTlv(Tlv::kExtendedPanId, Get<ExtendedPanIdManager>().GetExtPanId()));
337 }
338
339 if (dataset.GetTlv<MeshLocalPrefixTlv>() == nullptr)
340 {
341 IgnoreError(dataset.SetTlv(Tlv::kMeshLocalPrefix, Get<Mle::MleRouter>().GetMeshLocalPrefix()));
342 }
343
344 if (dataset.GetTlv<NetworkKeyTlv>() == nullptr)
345 {
346 NetworkKey networkKey;
347
348 Get<KeyManager>().GetNetworkKey(networkKey);
349 IgnoreError(dataset.SetTlv(Tlv::kNetworkKey, networkKey));
350 }
351
352 if (dataset.GetTlv<NetworkNameTlv>() == nullptr)
353 {
354 NameData nameData = Get<NetworkNameManager>().GetNetworkName().GetAsData();
355
356 IgnoreError(dataset.SetTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
357 }
358
359 if (dataset.GetTlv<PanIdTlv>() == nullptr)
360 {
361 IgnoreError(dataset.SetTlv(Tlv::kPanId, Get<Mac::Mac>().GetPanId()));
362 }
363
364 if (dataset.GetTlv<PskcTlv>() == nullptr)
365 {
366 Pskc pskc;
367
368 if (Get<KeyManager>().IsPskcSet())
369 {
370 Get<KeyManager>().GetPskc(pskc);
371 }
372 else
373 {
374 SuccessOrExit(error = pskc.GenerateRandom());
375 }
376
377 IgnoreError(dataset.SetTlv(Tlv::kPskc, pskc));
378 }
379
380 if (dataset.GetTlv<SecurityPolicyTlv>() == nullptr)
381 {
382 SecurityPolicyTlv tlv;
383
384 tlv.Init();
385 tlv.SetSecurityPolicy(Get<KeyManager>().GetSecurityPolicy());
386 IgnoreError(dataset.SetTlv(tlv));
387 }
388
389 SuccessOrExit(error = mLocal.Save(dataset));
390 IgnoreError(Restore());
391
392 LogInfo("Generated local dataset");
393
394 exit:
395 return error;
396 }
397
StartLeader(void)398 void ActiveDatasetManager::StartLeader(void) { IgnoreError(GenerateLocal()); }
399 #else // OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
StartLeader(void)400 void ActiveDatasetManager::StartLeader(void) {}
401 #endif // OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
402
403 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)404 void ActiveDatasetManager::HandleTmf<kUriActiveSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
405 {
406 VerifyOrExit(Get<Mle::Mle>().IsLeader());
407 SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
408 IgnoreError(ApplyConfiguration());
409
410 exit:
411 return;
412 }
413
StartLeader(void)414 void PendingDatasetManager::StartLeader(void) { StartDelayTimer(); }
415
416 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)417 void PendingDatasetManager::HandleTmf<kUriPendingSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
418 {
419 VerifyOrExit(Get<Mle::Mle>().IsLeader());
420 SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
421 StartDelayTimer();
422
423 exit:
424 return;
425 }
426
ApplyActiveDataset(const Timestamp & aTimestamp,Coap::Message & aMessage)427 void PendingDatasetManager::ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage)
428 {
429 uint16_t offset = aMessage.GetOffset();
430 Dataset dataset;
431
432 VerifyOrExit(Get<Mle::MleRouter>().IsAttached());
433
434 while (offset < aMessage.GetLength())
435 {
436 DatasetTlv datasetTlv;
437
438 SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
439 offset += static_cast<uint16_t>(datasetTlv.GetSize());
440 IgnoreError(dataset.SetTlv(datasetTlv));
441 }
442
443 // add delay timer tlv
444 IgnoreError(dataset.SetTlv(Tlv::kDelayTimer, Get<Leader>().GetDelayTimerMinimal()));
445
446 // add pending timestamp tlv
447 dataset.SetTimestamp(Dataset::kPending, aTimestamp);
448 IgnoreError(DatasetManager::Save(dataset));
449
450 // reset delay timer
451 StartDelayTimer();
452
453 exit:
454 return;
455 }
456
457 } // namespace MeshCoP
458 } // namespace ot
459
460 #endif // OPENTHREAD_FTD
461