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