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/locator_getters.hpp"
48 #include "common/log.hpp"
49 #include "common/random.hpp"
50 #include "common/timer.hpp"
51 #include "instance/instance.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 ChannelTlvValue channelValue;
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 if (Tlv::Find<ChannelTlv>(aMessage, channelValue) == kErrorNone)
120 {
121 VerifyOrExit(channelValue.IsValid());
122
123 if (channelValue.GetChannel() != Get<Mac::Mac>().GetPanChannel())
124 {
125 doesAffectConnectivity = true;
126 }
127 }
128
129 // check PAN ID
130 if (Tlv::Find<PanIdTlv>(aMessage, panId) == kErrorNone && panId != Get<Mac::Mac>().GetPanId())
131 {
132 doesAffectConnectivity = true;
133 }
134
135 // check mesh local prefix
136 if (Tlv::Find<MeshLocalPrefixTlv>(aMessage, meshLocalPrefix) == kErrorNone &&
137 meshLocalPrefix != Get<Mle::MleRouter>().GetMeshLocalPrefix())
138 {
139 doesAffectConnectivity = true;
140 }
141
142 // check network key
143 if (Tlv::Find<NetworkKeyTlv>(aMessage, networkKey) == kErrorNone)
144 {
145 NetworkKey localNetworkKey;
146
147 hasNetworkKey = true;
148 Get<KeyManager>().GetNetworkKey(localNetworkKey);
149
150 if (networkKey != localNetworkKey)
151 {
152 doesAffectConnectivity = true;
153 doesAffectNetworkKey = true;
154 }
155 }
156
157 // check active timestamp rollback
158 if (GetType() == Dataset::kPending && (!hasNetworkKey || !doesAffectNetworkKey))
159 {
160 // no change to network key, active timestamp must be ahead
161 const Timestamp *localActiveTimestamp = Get<ActiveDatasetManager>().GetTimestamp();
162
163 VerifyOrExit(Timestamp::Compare(&activeTimestamp, localActiveTimestamp) > 0);
164 }
165
166 // check commissioner session id
167 if (Tlv::Find<CommissionerSessionIdTlv>(aMessage, sessionId) == kErrorNone)
168 {
169 uint16_t localSessionId;
170
171 isUpdateFromCommissioner = true;
172
173 SuccessOrExit(Get<NetworkData::Leader>().FindCommissioningSessionId(localSessionId));
174 VerifyOrExit(localSessionId == sessionId);
175 }
176
177 // verify an MGMT_ACTIVE_SET.req from a Commissioner does not affect connectivity
178 VerifyOrExit(!isUpdateFromCommissioner || GetType() == Dataset::kPending || !doesAffectConnectivity);
179
180 if (isUpdateFromCommissioner)
181 {
182 // Thread specification allows partial dataset changes for MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req
183 // from Commissioner based on existing active dataset.
184 IgnoreError(Get<ActiveDatasetManager>().Read(dataset));
185 }
186
187 if (GetType() == Dataset::kPending || !doesAffectConnectivity)
188 {
189 offset = aMessage.GetOffset();
190
191 while (offset < aMessage.GetLength())
192 {
193 DatasetTlv datasetTlv;
194
195 SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
196
197 switch (datasetTlv.GetType())
198 {
199 case Tlv::kCommissionerSessionId:
200 // do not store Commissioner Session ID TLV
201 break;
202
203 case Tlv::kDelayTimer:
204 {
205 uint32_t delayTimer = datasetTlv.ReadValueAs<DelayTimerTlv>();
206
207 if (doesAffectNetworkKey && delayTimer < kDefaultDelayTimer)
208 {
209 delayTimer = kDefaultDelayTimer;
210 }
211 else
212 {
213 delayTimer = Max(delayTimer, Get<Leader>().GetDelayTimerMinimal());
214 }
215
216 datasetTlv.WriteValueAs<DelayTimerTlv>(delayTimer);
217 }
218
219 OT_FALL_THROUGH;
220
221 default:
222 SuccessOrExit(dataset.WriteTlv(datasetTlv));
223 break;
224 }
225
226 offset += static_cast<uint16_t>(datasetTlv.GetSize());
227 }
228
229 SuccessOrExit(Save(dataset));
230 Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
231 }
232 else
233 {
234 Get<PendingDatasetManager>().ApplyActiveDataset(activeTimestamp, aMessage);
235 }
236
237 state = StateTlv::kAccept;
238
239 // notify commissioner if update is from thread device
240 if (!isUpdateFromCommissioner)
241 {
242 uint16_t localSessionId;
243 Ip6::Address destination;
244
245 SuccessOrExit(Get<NetworkData::Leader>().FindCommissioningSessionId(localSessionId));
246 SuccessOrExit(Get<Mle::MleRouter>().GetCommissionerAloc(destination, localSessionId));
247 Get<Leader>().SendDatasetChanged(destination);
248 }
249
250 exit:
251
252 if (Get<Mle::MleRouter>().IsLeader())
253 {
254 SendSetResponse(aMessage, aMessageInfo, state);
255 }
256
257 return (state == StateTlv::kAccept) ? kErrorNone : kErrorDrop;
258 }
259
SendSetResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,StateTlv::State aState)260 void DatasetManager::SendSetResponse(const Coap::Message &aRequest,
261 const Ip6::MessageInfo &aMessageInfo,
262 StateTlv::State aState)
263 {
264 Error error = kErrorNone;
265 Coap::Message *message;
266
267 message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
268 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
269
270 SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
271
272 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
273
274 LogInfo("sent dataset set response");
275
276 exit:
277 FreeMessageOnError(message, error);
278 }
279
ReadFromMessage(const Message & aMessage,uint16_t aOffset)280 Error DatasetManager::DatasetTlv::ReadFromMessage(const Message &aMessage, uint16_t aOffset)
281 {
282 Error error = kErrorNone;
283
284 SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(Tlv)));
285 VerifyOrExit(GetLength() <= Dataset::kMaxValueSize, error = kErrorParse);
286 SuccessOrExit(error = aMessage.Read(aOffset + sizeof(Tlv), mValue, GetLength()));
287 VerifyOrExit(Tlv::IsValid(*this), error = kErrorParse);
288
289 exit:
290 return error;
291 }
292
293 #if OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
GenerateLocal(void)294 Error ActiveDatasetManager::GenerateLocal(void)
295 {
296 Error error = kErrorNone;
297 Dataset dataset;
298
299 VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = kErrorInvalidState);
300 VerifyOrExit(!mLocal.IsTimestampPresent(), error = kErrorAlready);
301
302 IgnoreError(Read(dataset));
303
304 if (!dataset.Contains<ActiveTimestampTlv>())
305 {
306 Timestamp timestamp;
307
308 timestamp.Clear();
309 IgnoreError(dataset.Write<ActiveTimestampTlv>(timestamp));
310 }
311
312 if (!dataset.Contains<ChannelTlv>())
313 {
314 ChannelTlvValue channelValue;
315
316 channelValue.SetChannelAndPage(Get<Mac::Mac>().GetPanChannel());
317 IgnoreError(dataset.Write<ChannelTlv>(channelValue));
318 }
319
320 if (!dataset.Contains<ChannelMaskTlv>())
321 {
322 ChannelMaskTlv::Value value;
323
324 ChannelMaskTlv::PrepareValue(value, Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
325 IgnoreError(dataset.WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
326 }
327
328 if (!dataset.Contains<ExtendedPanIdTlv>())
329 {
330 IgnoreError(dataset.Write<ExtendedPanIdTlv>(Get<ExtendedPanIdManager>().GetExtPanId()));
331 }
332
333 if (!dataset.Contains<MeshLocalPrefixTlv>())
334 {
335 IgnoreError(dataset.Write<MeshLocalPrefixTlv>(Get<Mle::MleRouter>().GetMeshLocalPrefix()));
336 }
337
338 if (!dataset.Contains<NetworkKeyTlv>())
339 {
340 NetworkKey networkKey;
341
342 Get<KeyManager>().GetNetworkKey(networkKey);
343 IgnoreError(dataset.Write<NetworkKeyTlv>(networkKey));
344 }
345
346 if (!dataset.Contains<NetworkNameTlv>())
347 {
348 NameData nameData = Get<NetworkNameManager>().GetNetworkName().GetAsData();
349
350 IgnoreError(dataset.WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
351 }
352
353 if (!dataset.Contains<PanIdTlv>())
354 {
355 IgnoreError(dataset.Write<PanIdTlv>(Get<Mac::Mac>().GetPanId()));
356 }
357
358 if (!dataset.Contains<PskcTlv>())
359 {
360 Pskc pskc;
361
362 if (Get<KeyManager>().IsPskcSet())
363 {
364 Get<KeyManager>().GetPskc(pskc);
365 }
366 else
367 {
368 SuccessOrExit(error = pskc.GenerateRandom());
369 }
370
371 IgnoreError(dataset.Write<PskcTlv>(pskc));
372 }
373
374 if (!dataset.Contains<SecurityPolicyTlv>())
375 {
376 SecurityPolicyTlv tlv;
377
378 tlv.Init();
379 tlv.SetSecurityPolicy(Get<KeyManager>().GetSecurityPolicy());
380 IgnoreError(dataset.WriteTlv(tlv));
381 }
382
383 SuccessOrExit(error = mLocal.Save(dataset));
384 IgnoreError(Restore());
385
386 LogInfo("Generated local dataset");
387
388 exit:
389 return error;
390 }
391
StartLeader(void)392 void ActiveDatasetManager::StartLeader(void) { IgnoreError(GenerateLocal()); }
393 #else // OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
StartLeader(void)394 void ActiveDatasetManager::StartLeader(void) {}
395 #endif // OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
396
397 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)398 void ActiveDatasetManager::HandleTmf<kUriActiveSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
399 {
400 VerifyOrExit(Get<Mle::Mle>().IsLeader());
401 SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
402 IgnoreError(ApplyConfiguration());
403
404 exit:
405 return;
406 }
407
StartLeader(void)408 void PendingDatasetManager::StartLeader(void) { StartDelayTimer(); }
409
410 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)411 void PendingDatasetManager::HandleTmf<kUriPendingSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
412 {
413 VerifyOrExit(Get<Mle::Mle>().IsLeader());
414 SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
415 StartDelayTimer();
416
417 exit:
418 return;
419 }
420
ApplyActiveDataset(const Timestamp & aTimestamp,Coap::Message & aMessage)421 void PendingDatasetManager::ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage)
422 {
423 uint16_t offset = aMessage.GetOffset();
424 Dataset dataset;
425
426 VerifyOrExit(Get<Mle::MleRouter>().IsAttached());
427
428 while (offset < aMessage.GetLength())
429 {
430 DatasetTlv datasetTlv;
431
432 SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset));
433 offset += static_cast<uint16_t>(datasetTlv.GetSize());
434 IgnoreError(dataset.WriteTlv(datasetTlv));
435 }
436
437 IgnoreError(dataset.Write<DelayTimerTlv>(Get<Leader>().GetDelayTimerMinimal()));
438
439 dataset.SetTimestamp(Dataset::kPending, aTimestamp);
440 IgnoreError(DatasetManager::Save(dataset));
441
442 StartDelayTimer();
443
444 exit:
445 return;
446 }
447
448 } // namespace MeshCoP
449 } // namespace ot
450
451 #endif // OPENTHREAD_FTD
452