• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
45 namespace ot {
46 namespace NetworkData {
47 
48 RegisterLogModule("NetworkData");
49 
Notifier(Instance & aInstance)50 Notifier::Notifier(Instance &aInstance)
51     : InstanceLocator(aInstance)
52     , mTimer(aInstance, HandleTimer)
53     , mSynchronizeDataTask(aInstance, HandleSynchronizeDataTask)
54     , mNextDelay(0)
55     , mWaitingForResponse(false)
56 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
57     , mDidRequestRouterRoleUpgrade(false)
58     , mRouterRoleUpgradeTimeout(0)
59 #endif
60 {
61 }
62 
HandleServerDataUpdated(void)63 void Notifier::HandleServerDataUpdated(void)
64 {
65 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
66     mDidRequestRouterRoleUpgrade = false;
67     ScheduleRouterRoleUpgradeIfEligible();
68 #endif
69 
70     mNextDelay = 0;
71     mSynchronizeDataTask.Post();
72 }
73 
HandleSynchronizeDataTask(Tasklet & aTasklet)74 void Notifier::HandleSynchronizeDataTask(Tasklet &aTasklet)
75 {
76     aTasklet.Get<Notifier>().SynchronizeServerData();
77 }
78 
SynchronizeServerData(void)79 void Notifier::SynchronizeServerData(void)
80 {
81     Error error = kErrorNotFound;
82 
83     VerifyOrExit(Get<Mle::MleRouter>().IsAttached() && !mWaitingForResponse);
84 
85     VerifyOrExit((mNextDelay == 0) || !mTimer.IsRunning());
86 
87 #if OPENTHREAD_FTD
88     mNextDelay = kDelayRemoveStaleChildren;
89     error      = Get<Leader>().RemoveStaleChildEntries(&Notifier::HandleCoapResponse, this);
90     VerifyOrExit(error == kErrorNotFound);
91 #endif
92 
93 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
94     mNextDelay = kDelaySynchronizeServerData;
95     error      = Get<Local>().UpdateInconsistentServerData(&Notifier::HandleCoapResponse, this);
96     VerifyOrExit(error == kErrorNotFound);
97 #endif
98 
99 exit:
100     switch (error)
101     {
102     case kErrorNone:
103         mWaitingForResponse = true;
104         break;
105     case kErrorNoBufs:
106         mTimer.Start(kDelayNoBufs);
107         break;
108 #if OPENTHREAD_FTD
109     case kErrorInvalidState:
110         mTimer.Start(Time::SecToMsec(Get<Mle::MleRouter>().GetRouterSelectionJitterTimeout() + 1));
111         break;
112 #endif
113     case kErrorNotFound:
114         break;
115     default:
116         OT_ASSERT(false);
117         OT_UNREACHABLE_CODE(break);
118     }
119 }
120 
HandleNotifierEvents(Events aEvents)121 void Notifier::HandleNotifierEvents(Events aEvents)
122 {
123     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadChildRemoved))
124     {
125         mNextDelay = 0;
126     }
127 
128 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
129     if (aEvents.Contains(kEventThreadPartitionIdChanged))
130     {
131         mDidRequestRouterRoleUpgrade = false;
132     }
133 
134     if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadNetdataChanged | kEventThreadPartitionIdChanged))
135     {
136         ScheduleRouterRoleUpgradeIfEligible();
137     }
138 #endif
139 
140     if (aEvents.ContainsAny(kEventThreadNetdataChanged | kEventThreadRoleChanged | kEventThreadChildRemoved))
141     {
142         SynchronizeServerData();
143     }
144 }
145 
HandleTimer(Timer & aTimer)146 void Notifier::HandleTimer(Timer &aTimer)
147 {
148     aTimer.Get<Notifier>().HandleTimer();
149 }
150 
HandleTimer(void)151 void Notifier::HandleTimer(void)
152 {
153     SynchronizeServerData();
154 }
155 
HandleCoapResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,Error aResult)156 void Notifier::HandleCoapResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult)
157 {
158     OT_UNUSED_VARIABLE(aMessage);
159     OT_UNUSED_VARIABLE(aMessageInfo);
160 
161     static_cast<Notifier *>(aContext)->HandleCoapResponse(aResult);
162 }
163 
HandleCoapResponse(Error aResult)164 void Notifier::HandleCoapResponse(Error aResult)
165 {
166     mWaitingForResponse = false;
167 
168     switch (aResult)
169     {
170     case kErrorNone:
171         mTimer.Start(mNextDelay + 1);
172         break;
173 
174     case kErrorResponseTimeout:
175     case kErrorAbort:
176         SynchronizeServerData();
177         break;
178 
179     default:
180         OT_ASSERT(false);
181         OT_UNREACHABLE_CODE(break);
182     }
183 }
184 
185 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
186 
IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const187 bool Notifier::IsEligibleForRouterRoleUpgradeAsBorderRouter(void) const
188 {
189     bool     isEligible = false;
190     uint16_t rloc16     = Get<Mle::Mle>().GetRloc16();
191     uint8_t  activeRouterCount;
192 
193     VerifyOrExit(Get<Mle::MleRouter>().IsRouterEligible());
194 
195     // RouterUpgradeThreshold can be explicitly set to zero in some of
196     // cert tests to disallow device to become router.
197 
198     VerifyOrExit(Get<Mle::MleRouter>().GetRouterUpgradeThreshold() != 0);
199 
200     // Check that we are a border router providing IP connectivity and already
201     // in the leader's network data and therefore eligible to request router
202     // role upgrade with `kBorderRouterRequest` status.
203 
204     VerifyOrExit(Get<Local>().ContainsBorderRouterWithRloc(rloc16) &&
205                  Get<Leader>().ContainsBorderRouterWithRloc(rloc16));
206 
207     activeRouterCount = Get<RouterTable>().GetActiveRouterCount();
208     VerifyOrExit((activeRouterCount >= Get<Mle::MleRouter>().GetRouterUpgradeThreshold()) &&
209                  (activeRouterCount < Mle::kMaxRouters));
210 
211     VerifyOrExit(Get<Leader>().CountBorderRouters(kRouterRoleOnly) < Mle::kRouterUpgradeBorderRouterRequestThreshold);
212     isEligible = true;
213 
214 exit:
215     return isEligible;
216 }
217 
ScheduleRouterRoleUpgradeIfEligible(void)218 void Notifier::ScheduleRouterRoleUpgradeIfEligible(void)
219 {
220     // We allow device to request router role upgrade using status
221     // reason `kBorderRouterRequest` once while its local network data
222     // remains unchanged. This ensures if the leader is running an
223     // older version of Thread stack which does not support
224     // `kBorderRouterRequest` reason, we do not keep trying (on no
225     // response). The boolean `mDidRequestRouterRoleUpgrade` tracks
226     // this. It is set to `false` when local network data gets changed
227     // or when partition ID gets changed (indicating a potential
228     // leader change).
229 
230     VerifyOrExit(!mDidRequestRouterRoleUpgrade);
231 
232     VerifyOrExit(Get<Mle::MleRouter>().IsChild());
233     VerifyOrExit(IsEligibleForRouterRoleUpgradeAsBorderRouter() && (mRouterRoleUpgradeTimeout == 0));
234 
235     mRouterRoleUpgradeTimeout = Random::NonCrypto::GetUint8InRange(1, kRouterRoleUpgradeMaxTimeout + 1);
236     Get<TimeTicker>().RegisterReceiver(TimeTicker::kNetworkDataNotifier);
237 
238 exit:
239     return;
240 }
241 
HandleTimeTick(void)242 void Notifier::HandleTimeTick(void)
243 {
244     VerifyOrExit(mRouterRoleUpgradeTimeout > 0);
245 
246     mRouterRoleUpgradeTimeout--;
247 
248     if (mRouterRoleUpgradeTimeout == 0)
249     {
250         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kNetworkDataNotifier);
251 
252         // Check that we are still eligible for requesting router role
253         // upgrade (note that state can change since the last time we
254         // checked and registered to receive time ticks).
255 
256         if (Get<Mle::MleRouter>().IsChild() && IsEligibleForRouterRoleUpgradeAsBorderRouter())
257         {
258             LogInfo("Requesting router role as BR");
259             mDidRequestRouterRoleUpgrade = true;
260             IgnoreError(Get<Mle::MleRouter>().BecomeRouter(ThreadStatusTlv::kBorderRouterRequest));
261         }
262     }
263 exit:
264     return;
265 }
266 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE &&
267        // OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE
268 
269 } // namespace NetworkData
270 } // namespace ot
271 
272 #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
273