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/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "common/random.hpp"
43 #include "instance/instance.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 , mDataset(nullptr)
52 {
53 }
54
RequestUpdate(const Dataset::Info & aDataset,UpdaterCallback aCallback,void * aContext)55 Error DatasetUpdater::RequestUpdate(const Dataset::Info &aDataset, UpdaterCallback aCallback, void *aContext)
56 {
57 Dataset dataset;
58
59 dataset.SetFrom(aDataset);
60 return RequestUpdate(dataset, aCallback, aContext);
61 }
62
RequestUpdate(Dataset & aDataset,UpdaterCallback aCallback,void * aContext)63 Error DatasetUpdater::RequestUpdate(Dataset &aDataset, UpdaterCallback aCallback, void *aContext)
64 {
65 Error error;
66 Message *message = nullptr;
67 Dataset activeDataset;
68 Timestamp activeTimestamp;
69 Timestamp pendingTimestamp;
70
71 error = kErrorInvalidState;
72 VerifyOrExit(!Get<Mle::Mle>().IsDisabled());
73 SuccessOrExit(Get<ActiveDatasetManager>().Read(activeDataset));
74 SuccessOrExit(activeDataset.Read<ActiveTimestampTlv>(activeTimestamp));
75
76 error = kErrorInvalidArgs;
77 SuccessOrExit(aDataset.ValidateTlvs());
78 VerifyOrExit(!aDataset.ContainsTlv(Tlv::kActiveTimestamp));
79 VerifyOrExit(!aDataset.ContainsTlv(Tlv::kPendingTimestamp));
80
81 VerifyOrExit(!IsUpdateOngoing(), error = kErrorBusy);
82
83 VerifyOrExit(!aDataset.IsSubsetOf(activeDataset), error = kErrorAlready);
84
85 // Set the Active and Pending Timestamps for new requested Dataset
86 // by advancing ticks on the current timestamp values.
87
88 activeTimestamp.AdvanceRandomTicks();
89 SuccessOrExit(error = aDataset.Write<ActiveTimestampTlv>(activeTimestamp));
90
91 pendingTimestamp = Get<PendingDatasetManager>().GetTimestamp();
92
93 if (!pendingTimestamp.IsValid())
94 {
95 pendingTimestamp.Clear();
96 }
97
98 pendingTimestamp.AdvanceRandomTicks();
99 SuccessOrExit(error = aDataset.Write<PendingTimestampTlv>(pendingTimestamp));
100
101 if (!aDataset.ContainsTlv(Tlv::kDelayTimer))
102 {
103 SuccessOrExit(error = aDataset.Write<DelayTimerTlv>(kDefaultDelay));
104 }
105
106 SuccessOrExit(error = activeDataset.WriteTlvsFrom(aDataset));
107
108 // Store the dataset in an allocated message to track update
109 // status and report the outcome via the callback.
110
111 message = Get<MessagePool>().Allocate(Message::kTypeOther);
112 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
113
114 SuccessOrExit(error = message->AppendBytes(aDataset.GetBytes(), aDataset.GetLength()));
115
116 Get<PendingDatasetManager>().SaveLocal(activeDataset);
117
118 mCallback.Set(aCallback, aContext);
119 mDataset = message;
120
121 exit:
122 FreeMessageOnError(message, error);
123 return error;
124 }
125
CancelUpdate(void)126 void DatasetUpdater::CancelUpdate(void)
127 {
128 VerifyOrExit(IsUpdateOngoing());
129
130 FreeMessage(mDataset);
131 mDataset = nullptr;
132
133 exit:
134 return;
135 }
136
Finish(Error aError)137 void DatasetUpdater::Finish(Error aError)
138 {
139 VerifyOrExit(IsUpdateOngoing());
140
141 FreeMessage(mDataset);
142 mDataset = nullptr;
143 mCallback.InvokeIfSet(aError);
144
145 exit:
146 return;
147 }
148
HandleNotifierEvents(Events aEvents)149 void DatasetUpdater::HandleNotifierEvents(Events aEvents)
150 {
151 if (aEvents.Contains(kEventActiveDatasetChanged))
152 {
153 HandleDatasetChanged(Dataset::kActive);
154 }
155
156 if (aEvents.Contains(kEventPendingDatasetChanged))
157 {
158 HandleDatasetChanged(Dataset::kPending);
159 }
160 }
161
HandleDatasetChanged(Dataset::Type aType)162 void DatasetUpdater::HandleDatasetChanged(Dataset::Type aType)
163 {
164 Dataset requestedDataset;
165 Dataset newDataset;
166 Timestamp newTimestamp;
167 Timestamp requestedTimestamp;
168 OffsetRange offsetRange;
169
170 VerifyOrExit(IsUpdateOngoing());
171
172 offsetRange.InitFromMessageFullLength(*mDataset);
173 SuccessOrExit(requestedDataset.SetFrom(*mDataset, offsetRange));
174
175 if (aType == Dataset::kActive)
176 {
177 SuccessOrExit(Get<ActiveDatasetManager>().Read(newDataset));
178 }
179 else
180 {
181 SuccessOrExit(Get<PendingDatasetManager>().Read(newDataset));
182 }
183
184 // Check if the new dataset includes the requested changes. If
185 // found in the Active Dataset, report success and finish. If
186 // found in the Pending Dataset, wait for it to be applied as
187 // Active.
188
189 if (requestedDataset.IsSubsetOf(newDataset))
190 {
191 if (aType == Dataset::kActive)
192 {
193 Finish(kErrorNone);
194 }
195
196 ExitNow();
197 }
198
199 // If the new timestamp is ahead of the requested timestamp, it
200 // means there was a conflicting update (possibly from another
201 // device). In this case, report the update as a failure.
202
203 SuccessOrExit(newDataset.ReadTimestamp(aType, newTimestamp));
204 SuccessOrExit(requestedDataset.ReadTimestamp(aType, requestedTimestamp));
205
206 if (newTimestamp >= requestedTimestamp)
207 {
208 Finish(kErrorAlready);
209 }
210
211 exit:
212 return;
213 }
214
215 } // namespace MeshCoP
216 } // namespace ot
217
218 #endif // #if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
219