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 transmissions of SVR_DATA.ntf messages to the Leader.
32 */
33
34 #include "network_data_notifier.hpp"
35
36 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
37
38 #include "common/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/log.hpp"
42 #include "thread/network_data_leader.hpp"
43 #include "thread/network_data_local.hpp"
44 #include "thread/tmf.hpp"
45 #include "thread/uri_paths.hpp"
46
47 namespace ot {
48 namespace NetworkData {
49
50 RegisterLogModule("NetworkData");
51
Notifier(Instance & aInstance)52 Notifier::Notifier(Instance &aInstance)
53 : InstanceLocator(aInstance)
54 , mTimer(aInstance)
55 , mSynchronizeDataTask(aInstance)
56 , mNextDelay(0)
57 , mOldRloc(Mac::kShortAddrInvalid)
58 , mWaitingForResponse(false)
59 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
60 , mDidRequestRouterRoleUpgrade(false)
61 , mRouterRoleUpgradeTimeout(0)
62 #endif
63 {
64 }
65
HandleServerDataUpdated(void)66 void Notifier::HandleServerDataUpdated(void)
67 {
68 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
69 mDidRequestRouterRoleUpgrade = false;
70 ScheduleRouterRoleUpgradeIfEligible();
71 #endif
72
73 mNextDelay = 0;
74 mSynchronizeDataTask.Post();
75 }
76
SynchronizeServerData(void)77 void Notifier::SynchronizeServerData(void)
78 {
79 Error error = kErrorNotFound;
80
81 VerifyOrExit(Get<Mle::MleRouter>().IsAttached() && !mWaitingForResponse);
82
83 VerifyOrExit((mNextDelay == 0) || !mTimer.IsRunning());
84
85 #if OPENTHREAD_FTD
86 mNextDelay = kDelayRemoveStaleChildren;
87 error = RemoveStaleChildEntries();
88 VerifyOrExit(error == kErrorNotFound);
89 #endif
90
91 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
92 mNextDelay = kDelaySynchronizeServerData;
93 error = UpdateInconsistentData();
94 VerifyOrExit(error == kErrorNotFound);
95 #endif
96
97 exit:
98 switch (error)
99 {
100 case kErrorNone:
101 mWaitingForResponse = true;
102 break;
103 case kErrorNoBufs:
104 mTimer.Start(kDelayNoBufs);
105 break;
106 #if OPENTHREAD_FTD
107 case kErrorInvalidState:
108 mTimer.Start(Time::SecToMsec(Get<Mle::MleRouter>().GetRouterSelectionJitterTimeout() + 1));
109 break;
110 #endif
111 case kErrorNotFound:
112 break;
113 default:
114 OT_ASSERT(false);
115 }
116 }
117
118 #if OPENTHREAD_FTD
RemoveStaleChildEntries(void)119 Error Notifier::RemoveStaleChildEntries(void)
120 {
121 // Check if there is any stale child entry in network data and send
122 // a "Server Data" notification to leader to remove it.
123 //
124 // - `kErrorNone` when a stale child entry was found and successfully
125 // sent a "Server Data" notification to leader.
126 // - `kErrorNoBufs` if could not allocate message to send message.
127 // - `kErrorNotFound` if no stale child entries were found.
128
129 Error error = kErrorNotFound;
130 Iterator iterator = kIteratorInit;
131 uint16_t rloc16;
132
133 VerifyOrExit(Get<Mle::MleRouter>().IsRouterOrLeader());
134
135 while (Get<Leader>().GetNextServer(iterator, rloc16) == kErrorNone)
136 {
137 if (!Mle::IsActiveRouter(rloc16) && Mle::RouterIdMatch(Get<Mle::MleRouter>().GetRloc16(), rloc16) &&
138 Get<ChildTable>().FindChild(rloc16, Child::kInStateValid) == nullptr)
139 {
140 error = SendServerDataNotification(rloc16);
141 ExitNow();
142 }
143 }
144
145 exit:
146 return error;
147 }
148 #endif // OPENTHREAD_FTD
149
150 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
UpdateInconsistentData(void)151 Error Notifier::UpdateInconsistentData(void)
152 {
153 Error error = kErrorNone;
154 uint16_t deviceRloc = Get<Mle::MleRouter>().GetRloc16();
155
156 #if OPENTHREAD_FTD
157 // Don't send this Server Data Notification if the device is going
158 // to upgrade to Router.
159
160 if (Get<Mle::MleRouter>().IsExpectedToBecomeRouterSoon())
161 {
162 ExitNow(error = kErrorInvalidState);
163 }
164 #endif
165
166 Get<Local>().UpdateRloc();
167
168 if (Get<Leader>().ContainsEntriesFrom(Get<Local>(), deviceRloc) &&
169 Get<Local>().ContainsEntriesFrom(Get<Leader>(), deviceRloc))
170 {
171 ExitNow(error = kErrorNotFound);
172 }
173
174 if (mOldRloc == deviceRloc)
175 {
176 mOldRloc = Mac::kShortAddrInvalid;
177 }
178
179 SuccessOrExit(error = SendServerDataNotification(mOldRloc, &Get<Local>()));
180 mOldRloc = deviceRloc;
181
182 exit:
183 return error;
184 }
185 #endif // #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
186
SendServerDataNotification(uint16_t aOldRloc16,const NetworkData * aNetworkData)187 Error Notifier::SendServerDataNotification(uint16_t aOldRloc16, const NetworkData *aNetworkData)
188 {
189 Error error = kErrorNone;
190 Coap::Message *message;
191 Tmf::MessageInfo messageInfo(GetInstance());
192
193 message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriServerData);
194 VerifyOrExit(message != nullptr, error = kErrorNoBufs);
195
196 if (aNetworkData != nullptr)
197 {
198 ThreadTlv tlv;
199
200 tlv.SetType(ThreadTlv::kThreadNetworkData);
201 tlv.SetLength(aNetworkData->GetLength());
202 SuccessOrExit(error = message->Append(tlv));
203 SuccessOrExit(error = message->AppendBytes(aNetworkData->GetBytes(), aNetworkData->GetLength()));
204 }
205
206 if (aOldRloc16 != Mac::kShortAddrInvalid)
207 {
208 SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, aOldRloc16));
209 }
210
211 IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
212 SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleCoapResponse, this));
213
214 LogInfo("Sent %s", UriToString<kUriServerData>());
215
216 exit:
217 FreeMessageOnError(message, error);
218 return error;
219 }
220
HandleNotifierEvents(Events aEvents)221 void Notifier::HandleNotifierEvents(Events aEvents)
222 {
223 if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadChildRemoved))
224 {
225 mNextDelay = 0;
226 }
227
228 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
229 if (aEvents.Contains(kEventThreadPartitionIdChanged))
230 {
231 mDidRequestRouterRoleUpgrade = false;
232 }
233
234 if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadNetdataChanged | kEventThreadPartitionIdChanged))
235 {
236 ScheduleRouterRoleUpgradeIfEligible();
237 }
238 #endif
239
240 if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged | kEventThreadChildRemoved))
241 {
242 SynchronizeServerData();
243 }
244 }
245
HandleTimer(void)246 void Notifier::HandleTimer(void) { SynchronizeServerData(); }
247
HandleCoapResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)248 void Notifier::HandleCoapResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
249 {
250 OT_UNUSED_VARIABLE(aMessage);
251 OT_UNUSED_VARIABLE(aMessageInfo);
252
253 static_cast<Notifier *>(aContext)->HandleCoapResponse(aResult);
254 }
255
HandleCoapResponse(Error aResult)256 void Notifier::HandleCoapResponse(Error aResult)
257 {
258 mWaitingForResponse = false;
259
260 switch (aResult)
261 {
262 case kErrorNone:
263 mTimer.Start(mNextDelay + 1);
264 break;
265
266 case kErrorResponseTimeout:
267 case kErrorAbort:
268 SynchronizeServerData();
269 break;
270
271 default:
272 OT_ASSERT(false);
273 }
274 }
275
276 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
277
IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const278 bool Notifier::IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const
279 {
280 bool isEligible = false;
281 uint16_t rloc16 = Get<Mle::Mle>().GetRloc16();
282 uint8_t activeRouterCount;
283
284 VerifyOrExit(Get<Mle::MleRouter>().IsRouterEligible());
285
286 // RouterUpgradeThreshold can be explicitly set to zero in some of
287 // cert tests to disallow device to become router.
288
289 VerifyOrExit(Get<Mle::MleRouter>().GetRouterUpgradeThreshold() != 0);
290
291 // Check that we are a border router providing IP connectivity and already
292 // in the leader's network data and therefore eligible to request router
293 // role upgrade with `kBorderRouterRequest` status.
294
295 VerifyOrExit(Get<Local>().ContainsBorderRouterWithRloc(rloc16) &&
296 Get<Leader>().ContainsBorderRouterWithRloc(rloc16));
297
298 activeRouterCount = Get<RouterTable>().GetActiveRouterCount();
299 VerifyOrExit((activeRouterCount >= Get<Mle::MleRouter>().GetRouterUpgradeThreshold()) &&
300 (activeRouterCount < Mle::kMaxRouters));
301
302 VerifyOrExit(Get<Leader>().CountBorderRouters(kRouterRoleOnly) < Mle::kRouterUpgradeBorderRouterRequestThreshold);
303 isEligible = true;
304
305 exit:
306 return isEligible;
307 }
308
ScheduleRouterRoleUpgradeIfEligible(void)309 void Notifier::ScheduleRouterRoleUpgradeIfEligible(void)
310 {
311 // We allow device to request router role upgrade using status
312 // reason `kBorderRouterRequest` once while its local network data
313 // remains unchanged. This ensures if the leader is running an
314 // older version of Thread stack which does not support
315 // `kBorderRouterRequest` reason, we do not keep trying (on no
316 // response). The boolean `mDidRequestRouterRoleUpgrade` tracks
317 // this. It is set to `false` when local network data gets changed
318 // or when partition ID gets changed (indicating a potential
319 // leader change).
320
321 VerifyOrExit(!mDidRequestRouterRoleUpgrade);
322
323 VerifyOrExit(Get<Mle::MleRouter>().IsChild());
324 VerifyOrExit(IsEligibleForRouterRoleUpgradeAsBorderRouter() && (mRouterRoleUpgradeTimeout == 0));
325
326 mRouterRoleUpgradeTimeout = Random::NonCrypto::GetUint8InRange(1, kRouterRoleUpgradeMaxTimeout + 1);
327 Get<TimeTicker>().RegisterReceiver(TimeTicker::kNetworkDataNotifier);
328
329 exit:
330 return;
331 }
332
HandleTimeTick(void)333 void Notifier::HandleTimeTick(void)
334 {
335 VerifyOrExit(mRouterRoleUpgradeTimeout > 0);
336
337 mRouterRoleUpgradeTimeout--;
338
339 if (mRouterRoleUpgradeTimeout == 0)
340 {
341 Get<TimeTicker>().UnregisterReceiver(TimeTicker::kNetworkDataNotifier);
342
343 // Check that we are still eligible for requesting router role
344 // upgrade (note that state can change since the last time we
345 // checked and registered to receive time ticks).
346
347 if (Get<Mle::MleRouter>().IsChild() && IsEligibleForRouterRoleUpgradeAsBorderRouter())
348 {
349 LogInfo("Requesting router role as BR");
350 mDidRequestRouterRoleUpgrade = true;
351 IgnoreError(Get<Mle::MleRouter>().BecomeRouter(ThreadStatusTlv::kBorderRouterRequest));
352 }
353 }
354 exit:
355 return;
356 }
357 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE &&
358 // OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
359
360 } // namespace NetworkData
361 } // namespace ot
362
363 #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
364