• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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