1 /*
2  *  Copyright (c) 2020, 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 Dataset Updater.
32  *
33  */
34 
35 #include "dataset_updater.hpp"
36 
37 #if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
38 
39 #include "common/code_utils.hpp"
40 #include "common/instance.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/logging.hpp"
43 #include "common/random.hpp"
44 
45 namespace ot {
46 namespace MeshCoP {
47 
DatasetUpdater(Instance & aInstance)48 DatasetUpdater::DatasetUpdater(Instance &aInstance)
49     : InstanceLocator(aInstance)
50     , mCallback(nullptr)
51     , mCallbackContext(nullptr)
52     , mTimer(aInstance, DatasetUpdater::HandleTimer)
53     , mDataset(nullptr)
54 {
55 }
56 
RequestUpdate(const MeshCoP::Dataset::Info & aDataset,Callback aCallback,void * aContext)57 Error DatasetUpdater::RequestUpdate(const MeshCoP::Dataset::Info &aDataset, Callback aCallback, void *aContext)
58 {
59     Error    error   = kErrorNone;
60     Message *message = nullptr;
61 
62     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
63     VerifyOrExit(mDataset == nullptr, error = kErrorBusy);
64 
65     VerifyOrExit(!aDataset.IsActiveTimestampPresent() && !aDataset.IsPendingTimestampPresent(),
66                  error = kErrorInvalidArgs);
67 
68     message = Get<MessagePool>().New(Message::kTypeOther, 0);
69     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
70 
71     SuccessOrExit(error = message->Append(aDataset));
72 
73     mCallback        = aCallback;
74     mCallbackContext = aContext;
75     mDataset         = message;
76 
77     mTimer.Start(1);
78 
79 exit:
80     FreeMessageOnError(message, error);
81     return error;
82 }
83 
CancelUpdate(void)84 void DatasetUpdater::CancelUpdate(void)
85 {
86     VerifyOrExit(mDataset != nullptr);
87 
88     FreeMessage(mDataset);
89     mDataset = nullptr;
90     mTimer.Stop();
91 
92 exit:
93     return;
94 }
95 
HandleTimer(Timer & aTimer)96 void DatasetUpdater::HandleTimer(Timer &aTimer)
97 {
98     aTimer.Get<DatasetUpdater>().HandleTimer();
99 }
100 
HandleTimer(void)101 void DatasetUpdater::HandleTimer(void)
102 {
103     PreparePendingDataset();
104 }
105 
PreparePendingDataset(void)106 void DatasetUpdater::PreparePendingDataset(void)
107 {
108     Dataset                dataset;
109     MeshCoP::Dataset::Info requestedDataset;
110     Error                  error;
111 
112     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
113 
114     IgnoreError(mDataset->Read(0, requestedDataset));
115 
116     error = Get<ActiveDataset>().Read(dataset);
117 
118     if (error != kErrorNone)
119     {
120         // If there is no valid Active Dataset but MLE is not disabled,
121         // set the timer to try again after the retry interval. This
122         // handles the situation where a dataset update request comes
123         // right after the network is formed but before the active
124         // dataset is created.
125 
126         mTimer.Start(kRetryInterval);
127         ExitNow(error = kErrorNone);
128     }
129 
130     IgnoreError(dataset.SetFrom(requestedDataset));
131 
132     if (!requestedDataset.IsDelayPresent())
133     {
134         uint32_t delay = kDefaultDelay;
135 
136         SuccessOrExit(error = dataset.SetTlv(Tlv::kDelayTimer, delay));
137     }
138 
139     {
140         Timestamp timestamp;
141 
142         if (Get<PendingDataset>().GetTimestamp() != nullptr)
143         {
144             timestamp = *Get<PendingDataset>().GetTimestamp();
145         }
146 
147         timestamp.AdvanceRandomTicks();
148         dataset.SetTimestamp(Dataset::kPending, timestamp);
149     }
150 
151     {
152         ActiveTimestampTlv *tlv = dataset.GetTlv<ActiveTimestampTlv>();
153         tlv->AdvanceRandomTicks();
154     }
155 
156     SuccessOrExit(error = Get<PendingDataset>().Save(dataset));
157 
158 exit:
159     if (error != kErrorNone)
160     {
161         Finish(error);
162     }
163 }
164 
Finish(Error aError)165 void DatasetUpdater::Finish(Error aError)
166 {
167     OT_ASSERT(mDataset != nullptr);
168 
169     FreeMessage(mDataset);
170     mDataset = nullptr;
171 
172     if (mCallback != nullptr)
173     {
174         mCallback(aError, mCallbackContext);
175     }
176 }
177 
HandleNotifierEvents(Events aEvents)178 void DatasetUpdater::HandleNotifierEvents(Events aEvents)
179 {
180     MeshCoP::Dataset::Info requestedDataset;
181     MeshCoP::Dataset::Info dataset;
182 
183     VerifyOrExit(mDataset != nullptr);
184 
185     VerifyOrExit(aEvents.ContainsAny(kEventActiveDatasetChanged | kEventPendingDatasetChanged));
186 
187     IgnoreError(mDataset->Read(0, requestedDataset));
188 
189     if (aEvents.Contains(kEventActiveDatasetChanged) && Get<MeshCoP::ActiveDataset>().Read(dataset) == kErrorNone)
190     {
191         if (requestedDataset.IsSubsetOf(dataset))
192         {
193             Finish(kErrorNone);
194         }
195         else if (requestedDataset.GetActiveTimestamp() <= dataset.GetActiveTimestamp())
196         {
197             Finish(kErrorAlready);
198         }
199     }
200 
201     if (aEvents.Contains(kEventPendingDatasetChanged) && Get<MeshCoP::PendingDataset>().Read(dataset) == kErrorNone)
202     {
203         if (!requestedDataset.IsSubsetOf(dataset))
204         {
205             Finish(kErrorAlready);
206         }
207     }
208 
209 exit:
210     return;
211 }
212 
213 } // namespace MeshCoP
214 } // namespace ot
215 
216 #endif // #if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
217