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/code_utils.hpp"
45 #include "common/debug.hpp"
46 #include "common/instance.hpp"
47 #include "common/locator_getters.hpp"
48 #include "common/logging.hpp"
49 #include "common/random.hpp"
50 #include "common/timer.hpp"
51 #include "meshcop/dataset.hpp"
52 #include "meshcop/meshcop.hpp"
53 #include "meshcop/meshcop_leader.hpp"
54 #include "meshcop/meshcop_tlvs.hpp"
55 #include "thread/thread_netif.hpp"
56 #include "thread/thread_tlvs.hpp"
57 #include "thread/uri_paths.hpp"
58
59 namespace ot {
60 namespace MeshCoP {
61
AppendMleDatasetTlv(Message & aMessage) const62 Error DatasetManager::AppendMleDatasetTlv(Message &aMessage) const
63 {
64 Dataset dataset;
65
66 IgnoreError(Read(dataset));
67
68 return dataset.AppendMleDatasetTlv(GetType(), aMessage);
69 }
70
HandleSet(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)71 Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
72 {
73 Tlv tlv;
74 Timestamp * timestamp;
75 uint16_t offset = aMessage.GetOffset();
76 Tlv::Type type;
77 bool isUpdateFromCommissioner = false;
78 bool doesAffectConnectivity = false;
79 bool doesAffectNetworkKey = false;
80 bool hasNetworkKey = false;
81 StateTlv::State state = StateTlv::kReject;
82 Dataset dataset;
83
84 ActiveTimestampTlv activeTimestamp;
85 PendingTimestampTlv pendingTimestamp;
86 ChannelTlv channel;
87 uint16_t sessionId;
88 Mle::MeshLocalPrefix meshLocalPrefix;
89 NetworkKey networkKey;
90 uint16_t panId;
91
92 activeTimestamp.SetLength(0);
93 pendingTimestamp.SetLength(0);
94 channel.SetLength(0);
95 pendingTimestamp.SetLength(0);
96
97 VerifyOrExit(Get<Mle::MleRouter>().IsLeader());
98
99 // verify that TLV data size is less than maximum TLV value size
100 while (offset < aMessage.GetLength())
101 {
102 SuccessOrExit(aMessage.Read(offset, tlv));
103 VerifyOrExit(tlv.GetLength() <= Dataset::kMaxValueSize);
104 offset += sizeof(tlv) + tlv.GetLength();
105 }
106
107 // verify that does not overflow dataset buffer
108 VerifyOrExit((offset - aMessage.GetOffset()) <= Dataset::kMaxSize);
109
110 type = (GetType() == Dataset::kActive) ? Tlv::kActiveTimestamp : Tlv::kPendingTimestamp;
111
112 if (Tlv::FindTlv(aMessage, activeTimestamp) != kErrorNone)
113 {
114 ExitNow();
115 }
116
117 VerifyOrExit(activeTimestamp.IsValid());
118
119 if (Tlv::FindTlv(aMessage, pendingTimestamp) == kErrorNone)
120 {
121 VerifyOrExit(pendingTimestamp.IsValid());
122 }
123
124 // verify the request includes a timestamp that is ahead of the locally stored value
125 timestamp = (type == Tlv::kActiveTimestamp) ? static_cast<Timestamp *>(&activeTimestamp)
126 : static_cast<Timestamp *>(&pendingTimestamp);
127
128 VerifyOrExit(mLocal.Compare(timestamp) > 0);
129
130 // check channel
131 if (Tlv::FindTlv(aMessage, channel) == kErrorNone)
132 {
133 VerifyOrExit(channel.IsValid());
134
135 if (channel.GetChannel() != Get<Mac::Mac>().GetPanChannel())
136 {
137 doesAffectConnectivity = true;
138 }
139 }
140
141 // check PAN ID
142 if (Tlv::Find<PanIdTlv>(aMessage, panId) == kErrorNone && panId != Get<Mac::Mac>().GetPanId())
143 {
144 doesAffectConnectivity = true;
145 }
146
147 // check mesh local prefix
148 if (Tlv::Find<MeshLocalPrefixTlv>(aMessage, meshLocalPrefix) == kErrorNone &&
149 meshLocalPrefix != Get<Mle::MleRouter>().GetMeshLocalPrefix())
150 {
151 doesAffectConnectivity = true;
152 }
153
154 // check network key
155 if (Tlv::Find<NetworkKeyTlv>(aMessage, networkKey) == kErrorNone)
156 {
157 hasNetworkKey = true;
158
159 if (networkKey != Get<KeyManager>().GetNetworkKey())
160 {
161 doesAffectConnectivity = true;
162 doesAffectNetworkKey = true;
163 }
164 }
165
166 // check active timestamp rollback
167 if (type == Tlv::kPendingTimestamp && (!hasNetworkKey || (networkKey == Get<KeyManager>().GetNetworkKey())))
168 {
169 // no change to network key, active timestamp must be ahead
170 const Timestamp *localActiveTimestamp = Get<ActiveDataset>().GetTimestamp();
171
172 VerifyOrExit(localActiveTimestamp == nullptr || localActiveTimestamp->Compare(activeTimestamp) > 0);
173 }
174
175 // check commissioner session id
176 if (Tlv::Find<CommissionerSessionIdTlv>(aMessage, sessionId) == kErrorNone)
177 {
178 const CommissionerSessionIdTlv *localId;
179
180 isUpdateFromCommissioner = true;
181
182 localId = static_cast<const CommissionerSessionIdTlv *>(
183 Get<NetworkData::Leader>().GetCommissioningDataSubTlv(Tlv::kCommissionerSessionId));
184
185 VerifyOrExit(localId != nullptr && localId->GetCommissionerSessionId() == sessionId);
186 }
187
188 // verify an MGMT_ACTIVE_SET.req from a Commissioner does not affect connectivity
189 VerifyOrExit(!isUpdateFromCommissioner || type == Tlv::kPendingTimestamp || !doesAffectConnectivity);
190
191 if (isUpdateFromCommissioner)
192 {
193 // Thread specification allows partial dataset changes for MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req
194 // from Commissioner based on existing active dataset.
195 IgnoreError(Get<ActiveDataset>().Read(dataset));
196 }
197
198 if (type == Tlv::kPendingTimestamp || !doesAffectConnectivity)
199 {
200 offset = aMessage.GetOffset();
201
202 while (offset < aMessage.GetLength())
203 {
204 DatasetTlv datasetTlv;
205
206 SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
207
208 switch (datasetTlv.GetType())
209 {
210 case Tlv::kCommissionerSessionId:
211 // do not store Commissioner Session ID TLV
212 break;
213
214 case Tlv::kDelayTimer:
215 {
216 DelayTimerTlv &delayTimerTlv = static_cast<DelayTimerTlv &>(static_cast<Tlv &>(datasetTlv));
217
218 if (doesAffectNetworkKey && delayTimerTlv.GetDelayTimer() < DelayTimerTlv::kDelayTimerDefault)
219 {
220 delayTimerTlv.SetDelayTimer(DelayTimerTlv::kDelayTimerDefault);
221 }
222 else if (delayTimerTlv.GetDelayTimer() < Get<Leader>().GetDelayTimerMinimal())
223 {
224 delayTimerTlv.SetDelayTimer(Get<Leader>().GetDelayTimerMinimal());
225 }
226 }
227
228 OT_FALL_THROUGH;
229
230 default:
231 SuccessOrExit(dataset.SetTlv(datasetTlv));
232 break;
233 }
234
235 offset += static_cast<uint16_t>(datasetTlv.GetSize());
236 }
237
238 SuccessOrExit(Save(dataset));
239 Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
240 }
241 else
242 {
243 Get<PendingDataset>().ApplyActiveDataset(activeTimestamp, aMessage);
244 }
245
246 state = StateTlv::kAccept;
247
248 // notify commissioner if update is from thread device
249 if (!isUpdateFromCommissioner)
250 {
251 const CommissionerSessionIdTlv *localSessionId;
252 Ip6::Address destination;
253
254 localSessionId = static_cast<const CommissionerSessionIdTlv *>(
255 Get<NetworkData::Leader>().GetCommissioningDataSubTlv(Tlv::kCommissionerSessionId));
256 VerifyOrExit(localSessionId != nullptr);
257
258 SuccessOrExit(
259 Get<Mle::MleRouter>().GetCommissionerAloc(destination, localSessionId->GetCommissionerSessionId()));
260
261 Get<Leader>().SendDatasetChanged(destination);
262 }
263
264 exit:
265
266 if (Get<Mle::MleRouter>().IsLeader())
267 {
268 SendSetResponse(aMessage, aMessageInfo, state);
269 }
270
271 return (state == StateTlv::kAccept) ? kErrorNone : kErrorDrop;
272 }
273
SendSetResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,StateTlv::State aState)274 void DatasetManager::SendSetResponse(const Coap::Message & aRequest,
275 const Ip6::MessageInfo &aMessageInfo,
276 StateTlv::State aState)
277 {
278 Error error = kErrorNone;
279 Coap::Message *message;
280
281 VerifyOrExit((message = NewMeshCoPMessage(Get<Tmf::Agent>())) != nullptr, error = kErrorNoBufs);
282
283 SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
284 SuccessOrExit(error = message->SetPayloadMarker());
285
286 SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
287
288 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
289
290 otLogInfoMeshCoP("sent dataset set response");
291
292 exit:
293 FreeMessageOnError(message, error);
294 }
295
ReadFromMessage(const Message & aMessage,uint16_t aOffset)296 Error DatasetManager::DatasetTlv::ReadFromMessage(const Message &aMessage, uint16_t aOffset)
297 {
298 Error error = kErrorNone;
299
300 SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(Tlv)));
301 VerifyOrExit(GetLength() <= Dataset::kMaxValueSize, error = kErrorParse);
302 SuccessOrExit(error = aMessage.Read(aOffset + sizeof(Tlv), mValue, GetLength()));
303 VerifyOrExit(Tlv::IsValid(*this), error = kErrorParse);
304
305 exit:
306 return error;
307 }
308
GenerateLocal(void)309 Error ActiveDataset::GenerateLocal(void)
310 {
311 Error error = kErrorNone;
312 Dataset dataset;
313
314 VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = kErrorInvalidState);
315 VerifyOrExit(!mLocal.IsTimestampPresent(), error = kErrorAlready);
316
317 IgnoreError(Read(dataset));
318
319 if (dataset.GetTlv<ActiveTimestampTlv>() == nullptr)
320 {
321 ActiveTimestampTlv activeTimestampTlv;
322 activeTimestampTlv.Init();
323 activeTimestampTlv.SetSeconds(0);
324 activeTimestampTlv.SetTicks(0);
325 IgnoreError(dataset.SetTlv(activeTimestampTlv));
326 }
327
328 if (dataset.GetTlv<ChannelTlv>() == nullptr)
329 {
330 ChannelTlv tlv;
331 tlv.Init();
332 tlv.SetChannel(Get<Mac::Mac>().GetPanChannel());
333 IgnoreError(dataset.SetTlv(tlv));
334 }
335
336 if (dataset.GetTlv<ChannelMaskTlv>() == nullptr)
337 {
338 ChannelMaskTlv tlv;
339 tlv.Init();
340 tlv.SetChannelMask(Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
341 IgnoreError(dataset.SetTlv(tlv));
342 }
343
344 if (dataset.GetTlv<ExtendedPanIdTlv>() == nullptr)
345 {
346 IgnoreError(dataset.SetTlv(Tlv::kExtendedPanId, Get<Mac::Mac>().GetExtendedPanId()));
347 }
348
349 if (dataset.GetTlv<MeshLocalPrefixTlv>() == nullptr)
350 {
351 IgnoreError(dataset.SetTlv(Tlv::kMeshLocalPrefix, Get<Mle::MleRouter>().GetMeshLocalPrefix()));
352 }
353
354 if (dataset.GetTlv<NetworkKeyTlv>() == nullptr)
355 {
356 IgnoreError(dataset.SetTlv(Tlv::kNetworkKey, Get<KeyManager>().GetNetworkKey()));
357 }
358
359 if (dataset.GetTlv<NetworkNameTlv>() == nullptr)
360 {
361 Mac::NameData nameData = Get<Mac::Mac>().GetNetworkName().GetAsData();
362
363 IgnoreError(dataset.SetTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
364 }
365
366 if (dataset.GetTlv<PanIdTlv>() == nullptr)
367 {
368 IgnoreError(dataset.SetTlv(Tlv::kPanId, Get<Mac::Mac>().GetPanId()));
369 }
370
371 if (dataset.GetTlv<PskcTlv>() == nullptr)
372 {
373 if (Get<KeyManager>().IsPskcSet())
374 {
375 IgnoreError(dataset.SetTlv(Tlv::kPskc, Get<KeyManager>().GetPskc()));
376 }
377 else
378 {
379 // PSKc has not yet been configured, generate new PSKc at random
380 Pskc pskc;
381
382 SuccessOrExit(error = pskc.GenerateRandom());
383 IgnoreError(dataset.SetTlv(Tlv::kPskc, pskc));
384 }
385 }
386
387 if (dataset.GetTlv<SecurityPolicyTlv>() == nullptr)
388 {
389 SecurityPolicyTlv tlv;
390
391 tlv.Init();
392 tlv.SetSecurityPolicy(Get<KeyManager>().GetSecurityPolicy());
393 IgnoreError(dataset.SetTlv(tlv));
394 }
395
396 SuccessOrExit(error = mLocal.Save(dataset));
397 IgnoreError(Restore());
398
399 otLogInfoMeshCoP("Generated local dataset");
400
401 exit:
402 return error;
403 }
404
StartLeader(void)405 void ActiveDataset::StartLeader(void)
406 {
407 IgnoreError(GenerateLocal());
408 Get<Tmf::Agent>().AddResource(mResourceSet);
409 }
410
StopLeader(void)411 void ActiveDataset::StopLeader(void)
412 {
413 Get<Tmf::Agent>().RemoveResource(mResourceSet);
414 }
415
HandleSet(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)416 void ActiveDataset::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
417 {
418 static_cast<ActiveDataset *>(aContext)->HandleSet(*static_cast<Coap::Message *>(aMessage),
419 *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
420 }
421
HandleSet(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)422 void ActiveDataset::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
423 {
424 SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
425 IgnoreError(ApplyConfiguration());
426
427 exit:
428 return;
429 }
430
StartLeader(void)431 void PendingDataset::StartLeader(void)
432 {
433 StartDelayTimer();
434 Get<Tmf::Agent>().AddResource(mResourceSet);
435 }
436
StopLeader(void)437 void PendingDataset::StopLeader(void)
438 {
439 Get<Tmf::Agent>().RemoveResource(mResourceSet);
440 }
441
HandleSet(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)442 void PendingDataset::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
443 {
444 static_cast<PendingDataset *>(aContext)->HandleSet(*static_cast<Coap::Message *>(aMessage),
445 *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
446 }
447
HandleSet(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)448 void PendingDataset::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
449 {
450 SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
451 StartDelayTimer();
452
453 exit:
454 return;
455 }
456
ApplyActiveDataset(const Timestamp & aTimestamp,Coap::Message & aMessage)457 void PendingDataset::ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage)
458 {
459 uint16_t offset = aMessage.GetOffset();
460 Dataset dataset;
461
462 VerifyOrExit(Get<Mle::MleRouter>().IsAttached());
463
464 while (offset < aMessage.GetLength())
465 {
466 DatasetTlv datasetTlv;
467
468 SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
469 offset += static_cast<uint16_t>(datasetTlv.GetSize());
470 IgnoreError(dataset.SetTlv(datasetTlv));
471 }
472
473 // add delay timer tlv
474 IgnoreError(dataset.SetTlv(Tlv::kDelayTimer, Get<Leader>().GetDelayTimerMinimal()));
475
476 // add pending timestamp tlv
477 dataset.SetTimestamp(Dataset::kPending, aTimestamp);
478 IgnoreError(DatasetManager::Save(dataset));
479
480 // reset delay timer
481 StartDelayTimer();
482
483 exit:
484 return;
485 }
486
487 } // namespace MeshCoP
488 } // namespace ot
489
490 #endif // OPENTHREAD_FTD
491