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
65 //----------------------------------------------------------------------------------------------------------------------
66 // DatasetManager
67
ProcessSetOrReplaceRequest(MgmtCommand aCommand,const Coap::Message & aMessage,RequestInfo & aInfo) const68 Error DatasetManager::ProcessSetOrReplaceRequest(MgmtCommand aCommand,
69 const Coap::Message &aMessage,
70 RequestInfo &aInfo) const
71 {
72 Error error = kErrorParse;
73 Dataset dataset;
74 OffsetRange offsetRange;
75 Timestamp activeTimestamp;
76 ChannelTlvValue channelValue;
77 uint16_t sessionId;
78 Ip6::NetworkPrefix meshLocalPrefix;
79 NetworkKey networkKey;
80 uint16_t panId;
81 uint32_t delayTimer;
82
83 aInfo.Clear();
84
85 offsetRange.InitFromMessageOffsetToEnd(aMessage);
86 SuccessOrExit(dataset.SetFrom(aMessage, offsetRange));
87 SuccessOrExit(dataset.ValidateTlvs());
88
89 // Verify that the request includes timestamps that are
90 // ahead of the locally stored values.
91
92 SuccessOrExit(dataset.Read<ActiveTimestampTlv>(activeTimestamp));
93
94 if (IsPendingDataset())
95 {
96 Timestamp pendingTimestamp;
97
98 SuccessOrExit(dataset.Read<PendingTimestampTlv>(pendingTimestamp));
99 VerifyOrExit(pendingTimestamp > mLocalTimestamp);
100 }
101 else
102 {
103 VerifyOrExit(activeTimestamp > mLocalTimestamp);
104 }
105
106 // Determine whether the new Dataset affects connectivity
107 // or network key.
108
109 if ((dataset.Read<ChannelTlv>(channelValue) == kErrorNone) &&
110 (channelValue.GetChannel() != Get<Mac::Mac>().GetPanChannel()))
111 {
112 aInfo.mAffectsConnectivity = true;
113 }
114
115 if ((dataset.Read<PanIdTlv>(panId) == kErrorNone) && (panId != Get<Mac::Mac>().GetPanId()))
116 {
117 aInfo.mAffectsConnectivity = true;
118 }
119
120 if ((dataset.Read<MeshLocalPrefixTlv>(meshLocalPrefix) == kErrorNone) &&
121 (meshLocalPrefix != Get<Mle::MleRouter>().GetMeshLocalPrefix()))
122 {
123 aInfo.mAffectsConnectivity = true;
124 }
125
126 if (dataset.Read<NetworkKeyTlv>(networkKey) == kErrorNone)
127 {
128 NetworkKey localNetworkKey;
129
130 Get<KeyManager>().GetNetworkKey(localNetworkKey);
131
132 if (networkKey != localNetworkKey)
133 {
134 aInfo.mAffectsConnectivity = true;
135 aInfo.mAffectsNetworkKey = true;
136 }
137 }
138
139 // Check active timestamp rollback. If there is no change to
140 // network key, active timestamp must be ahead of local value.
141
142 if (IsPendingDataset() && !aInfo.mAffectsNetworkKey)
143 {
144 VerifyOrExit(activeTimestamp > Get<ActiveDatasetManager>().GetTimestamp());
145 }
146
147 // Determine whether the request is from commissioner.
148
149 if (dataset.Read<CommissionerSessionIdTlv>(sessionId) == kErrorNone)
150 {
151 uint16_t localSessionId;
152
153 aInfo.mIsFromCommissioner = true;
154
155 dataset.RemoveTlv(Tlv::kCommissionerSessionId);
156
157 SuccessOrExit(Get<NetworkData::Leader>().FindCommissioningSessionId(localSessionId));
158 VerifyOrExit(localSessionId == sessionId);
159
160 // Verify an MGMT_ACTIVE_SET.req from a Commissioner does not
161 // affect connectivity.
162
163 if (IsActiveDataset())
164 {
165 VerifyOrExit(!aInfo.mAffectsConnectivity);
166 }
167
168 // Thread specification allows partial dataset changes for
169 // MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req from Commissioner
170 // based on existing active dataset.
171
172 if (aCommand == kMgmtSet)
173 {
174 IgnoreError(Get<ActiveDatasetManager>().Read(aInfo.mDataset));
175 }
176 }
177
178 if (aCommand == kMgmtReplace)
179 {
180 // MGMT_ACTIVE_REPLACE can only be used by commissioner.
181
182 VerifyOrExit(aInfo.mIsFromCommissioner);
183 VerifyOrExit(IsActiveDataset());
184 VerifyOrExit(dataset.ContainsAllRequiredTlvsFor(Dataset::kActive));
185 }
186
187 SuccessOrExit(error = aInfo.mDataset.WriteTlvsFrom(dataset));
188
189 // Check and update the Delay Timer TLV value if present.
190
191 if (aInfo.mDataset.Read<DelayTimerTlv>(delayTimer) == kErrorNone)
192 {
193 delayTimer = Min(delayTimer, DelayTimerTlv::kMaxDelay);
194
195 if (aInfo.mAffectsNetworkKey && (delayTimer < DelayTimerTlv::kDefaultDelay))
196 {
197 delayTimer = DelayTimerTlv::kDefaultDelay;
198 }
199 else
200 {
201 delayTimer = Max(delayTimer, Get<Leader>().GetDelayTimerMinimal());
202 }
203
204 IgnoreError(aInfo.mDataset.Write<DelayTimerTlv>(delayTimer));
205 }
206
207 exit:
208 return error;
209 }
210
HandleSetOrReplace(MgmtCommand aCommand,const Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)211 Error DatasetManager::HandleSetOrReplace(MgmtCommand aCommand,
212 const Coap::Message &aMessage,
213 const Ip6::MessageInfo &aMessageInfo)
214 {
215 StateTlv::State state = StateTlv::kReject;
216 RequestInfo info;
217
218 VerifyOrExit(Get<Mle::Mle>().IsLeader());
219
220 SuccessOrExit(ProcessSetOrReplaceRequest(aCommand, aMessage, info));
221
222 if (IsActiveDataset() && info.mAffectsConnectivity)
223 {
224 // MGMT_ACTIVE_SET/REPLACE.req which affects
225 // connectivity MUST be delayed using pending
226 // dataset.
227
228 Get<PendingDatasetManager>().ApplyActiveDataset(info.mDataset);
229 }
230 else
231 {
232 SuccessOrExit(Save(info.mDataset));
233 Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
234 }
235
236 state = StateTlv::kAccept;
237
238 // Notify commissioner if update is from a Thread device.
239
240 if (!info.mIsFromCommissioner)
241 {
242 uint16_t localSessionId;
243 Ip6::Address destination;
244
245 SuccessOrExit(Get<NetworkData::Leader>().FindCommissioningSessionId(localSessionId));
246 Get<Mle::Mle>().GetCommissionerAloc(localSessionId, destination);
247 Get<Leader>().SendDatasetChanged(destination);
248 }
249
250 exit:
251 SendSetOrReplaceResponse(aMessage, aMessageInfo, state);
252
253 return (state == StateTlv::kAccept) ? kErrorNone : kErrorDrop;
254 }
255
SendSetOrReplaceResponse(const Coap::Message & aRequest,const Ip6::MessageInfo & aMessageInfo,StateTlv::State aState)256 void DatasetManager::SendSetOrReplaceResponse(const Coap::Message &aRequest,
257 const Ip6::MessageInfo &aMessageInfo,
258 StateTlv::State aState)
259 {
260 Error error = kErrorNone;
261 Coap::Message *message;
262
263 message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
264 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
265
266 SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
267
268 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
269
270 LogInfo("sent dataset set/replace response");
271
272 exit:
273 FreeMessageOnError(message, error);
274 }
275
276 //----------------------------------------------------------------------------------------------------------------------
277 // ActiveDatasetManager
278
279 #if OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
GenerateLocal(void)280 Error ActiveDatasetManager::GenerateLocal(void)
281 {
282 Error error = kErrorNone;
283 Dataset dataset;
284
285 VerifyOrExit(Get<Mle::MleRouter>().IsAttached(), error = kErrorInvalidState);
286 VerifyOrExit(!mLocalTimestamp.IsValid(), error = kErrorAlready);
287
288 IgnoreError(Read(dataset));
289
290 if (!dataset.Contains<ActiveTimestampTlv>())
291 {
292 Timestamp timestamp;
293
294 timestamp.Clear();
295 IgnoreError(dataset.Write<ActiveTimestampTlv>(timestamp));
296 }
297
298 if (!dataset.Contains<ChannelTlv>())
299 {
300 ChannelTlvValue channelValue;
301
302 channelValue.SetChannelAndPage(Get<Mac::Mac>().GetPanChannel());
303 IgnoreError(dataset.Write<ChannelTlv>(channelValue));
304 }
305
306 if (!dataset.Contains<ChannelMaskTlv>())
307 {
308 ChannelMaskTlv::Value value;
309
310 ChannelMaskTlv::PrepareValue(value, Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
311 IgnoreError(dataset.WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
312 }
313
314 if (!dataset.Contains<ExtendedPanIdTlv>())
315 {
316 IgnoreError(dataset.Write<ExtendedPanIdTlv>(Get<ExtendedPanIdManager>().GetExtPanId()));
317 }
318
319 if (!dataset.Contains<MeshLocalPrefixTlv>())
320 {
321 IgnoreError(dataset.Write<MeshLocalPrefixTlv>(Get<Mle::MleRouter>().GetMeshLocalPrefix()));
322 }
323
324 if (!dataset.Contains<NetworkKeyTlv>())
325 {
326 NetworkKey networkKey;
327
328 Get<KeyManager>().GetNetworkKey(networkKey);
329 IgnoreError(dataset.Write<NetworkKeyTlv>(networkKey));
330 }
331
332 if (!dataset.Contains<NetworkNameTlv>())
333 {
334 NameData nameData = Get<NetworkNameManager>().GetNetworkName().GetAsData();
335
336 IgnoreError(dataset.WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
337 }
338
339 if (!dataset.Contains<PanIdTlv>())
340 {
341 IgnoreError(dataset.Write<PanIdTlv>(Get<Mac::Mac>().GetPanId()));
342 }
343
344 if (!dataset.Contains<PskcTlv>())
345 {
346 Pskc pskc;
347
348 if (Get<KeyManager>().IsPskcSet())
349 {
350 Get<KeyManager>().GetPskc(pskc);
351 }
352 else
353 {
354 SuccessOrExit(error = pskc.GenerateRandom());
355 }
356
357 IgnoreError(dataset.Write<PskcTlv>(pskc));
358 }
359
360 if (!dataset.Contains<SecurityPolicyTlv>())
361 {
362 SecurityPolicyTlv tlv;
363
364 tlv.Init();
365 tlv.SetSecurityPolicy(Get<KeyManager>().GetSecurityPolicy());
366 IgnoreError(dataset.WriteTlv(tlv));
367 }
368
369 LocalSave(dataset);
370 Restore(dataset);
371
372 LogInfo("Generated local dataset");
373
374 exit:
375 return error;
376 }
377
StartLeader(void)378 void ActiveDatasetManager::StartLeader(void) { IgnoreError(GenerateLocal()); }
379 #else // OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
StartLeader(void)380 void ActiveDatasetManager::StartLeader(void) {}
381 #endif // OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT
382
383 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)384 void ActiveDatasetManager::HandleTmf<kUriActiveSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
385 {
386 SuccessOrExit(DatasetManager::HandleSetOrReplace(kMgmtSet, aMessage, aMessageInfo));
387 IgnoreError(ApplyConfiguration());
388
389 exit:
390 return;
391 }
392
393 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)394 void ActiveDatasetManager::HandleTmf<kUriActiveReplace>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
395 {
396 SuccessOrExit(DatasetManager::HandleSetOrReplace(kMgmtReplace, aMessage, aMessageInfo));
397 IgnoreError(ApplyConfiguration());
398
399 exit:
400 return;
401 }
402
403 //----------------------------------------------------------------------------------------------------------------------
404 // PendingDatasetManager
405
StartLeader(void)406 void PendingDatasetManager::StartLeader(void) { StartDelayTimer(); }
407
408 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)409 void PendingDatasetManager::HandleTmf<kUriPendingSet>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
410 {
411 SuccessOrExit(DatasetManager::HandleSetOrReplace(kMgmtSet, aMessage, aMessageInfo));
412 StartDelayTimer();
413
414 exit:
415 return;
416 }
417
ApplyActiveDataset(Dataset & aDataset)418 void PendingDatasetManager::ApplyActiveDataset(Dataset &aDataset)
419 {
420 // Generates and applies Pending Dataset from an Active Dataset.
421
422 Timestamp activeTimestamp;
423
424 SuccessOrExit(aDataset.Read<ActiveTimestampTlv>(activeTimestamp));
425 SuccessOrExit(aDataset.Write<PendingTimestampTlv>(activeTimestamp));
426 SuccessOrExit(aDataset.Write<DelayTimerTlv>(Get<Leader>().GetDelayTimerMinimal()));
427
428 IgnoreError(DatasetManager::Save(aDataset));
429 StartDelayTimer(aDataset);
430
431 exit:
432 return;
433 }
434
435 } // namespace MeshCoP
436 } // namespace ot
437
438 #endif // OPENTHREAD_FTD
439