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