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