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