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 Thread NdProxy Table management.
32 */
33
34 #include "ndproxy_table.hpp"
35
36 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
37
38 #include "common/locator_getters.hpp"
39 #include "common/logging.hpp"
40
41 namespace ot {
42
43 namespace BackboneRouter {
44
Init(const Ip6::InterfaceIdentifier & aAddressIid,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint16_t aRloc16,uint32_t aTimeSinceLastTransaction)45 void NdProxyTable::NdProxy::Init(const Ip6::InterfaceIdentifier &aAddressIid,
46 const Ip6::InterfaceIdentifier &aMeshLocalIid,
47 uint16_t aRloc16,
48 uint32_t aTimeSinceLastTransaction)
49 {
50 OT_ASSERT(!mValid);
51
52 Clear();
53
54 mValid = true;
55 mAddressIid = aAddressIid;
56 mMeshLocalIid = aMeshLocalIid;
57 mDadFlag = true;
58
59 Update(aRloc16, aTimeSinceLastTransaction);
60 }
61
Update(uint16_t aRloc16,uint32_t aTimeSinceLastTransaction)62 void NdProxyTable::NdProxy::Update(uint16_t aRloc16, uint32_t aTimeSinceLastTransaction)
63 {
64 OT_ASSERT(mValid);
65
66 mRloc16 = aRloc16;
67 aTimeSinceLastTransaction =
68 OT_MIN(aTimeSinceLastTransaction, static_cast<uint32_t>(Mle::kTimeSinceLastTransactionMax));
69 mLastRegistrationTime = TimerMilli::GetNow() - TimeMilli::SecToMsec(aTimeSinceLastTransaction);
70 }
71
MatchesFilter(const NdProxy & aProxy,Filter aFilter)72 bool NdProxyTable::MatchesFilter(const NdProxy &aProxy, Filter aFilter)
73 {
74 bool rval = false;
75
76 switch (aFilter)
77 {
78 case kFilterInvalid:
79 rval = !aProxy.mValid;
80 break;
81 case kFilterValid:
82 rval = aProxy.mValid;
83 break;
84 case kFilterDadInProcess:
85 rval = aProxy.mValid && aProxy.mDadFlag;
86 break;
87 }
88
89 return rval;
90 }
91
Iterator(Instance & aInstance,Filter aFilter)92 NdProxyTable::Iterator::Iterator(Instance &aInstance, Filter aFilter)
93 : InstanceLocator(aInstance)
94 , mFilter(aFilter)
95 {
96 NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>();
97
98 mItem = &table.mProxies[0];
99
100 if (!MatchesFilter(*mItem, mFilter))
101 {
102 Advance();
103 }
104 }
105
Iterator(Instance & aInstance,NdProxyTable::Iterator::IteratorType)106 NdProxyTable::Iterator::Iterator(Instance &aInstance, NdProxyTable::Iterator::IteratorType)
107 : InstanceLocator(aInstance)
108 {
109 NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>();
110 mItem = OT_ARRAY_END(table.mProxies);
111 }
112
Advance(void)113 void NdProxyTable::Iterator::Advance(void)
114 {
115 NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>();
116
117 do
118 {
119 mItem++;
120 } while (mItem < OT_ARRAY_END(table.mProxies) && !MatchesFilter(*mItem, mFilter));
121 }
122
Erase(NdProxy & aNdProxy)123 void NdProxyTable::Erase(NdProxy &aNdProxy)
124 {
125 aNdProxy.mValid = false;
126 }
127
HandleDomainPrefixUpdate(Leader::DomainPrefixState aState)128 void NdProxyTable::HandleDomainPrefixUpdate(Leader::DomainPrefixState aState)
129 {
130 if (aState == Leader::kDomainPrefixAdded || aState == Leader::kDomainPrefixRemoved ||
131 aState == Leader::kDomainPrefixRefreshed)
132 {
133 Clear();
134 }
135 }
136
Clear(void)137 void NdProxyTable::Clear(void)
138 {
139 for (NdProxy &proxy : mProxies)
140 {
141 proxy.Clear();
142 }
143
144 if (mCallback != nullptr)
145 {
146 mCallback(mCallbackContext, OT_BACKBONE_ROUTER_NDPROXY_CLEARED, nullptr);
147 }
148
149 otLogNoteBbr("NdProxyTable::Clear!");
150 }
151
Register(const Ip6::InterfaceIdentifier & aAddressIid,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint16_t aRloc16,const uint32_t * aTimeSinceLastTransaction)152 Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid,
153 const Ip6::InterfaceIdentifier &aMeshLocalIid,
154 uint16_t aRloc16,
155 const uint32_t * aTimeSinceLastTransaction)
156 {
157 Error error = kErrorNone;
158 NdProxy *proxy = FindByAddressIid(aAddressIid);
159 uint32_t timeSinceLastTransaction = aTimeSinceLastTransaction == nullptr ? 0 : *aTimeSinceLastTransaction;
160
161 if (proxy != nullptr)
162 {
163 VerifyOrExit(proxy->mMeshLocalIid == aMeshLocalIid, error = kErrorDuplicated);
164
165 proxy->Update(aRloc16, timeSinceLastTransaction);
166 NotifyDuaRegistrationOnBackboneLink(*proxy, /* aIsRenew */ true);
167 ExitNow();
168 }
169
170 proxy = FindByMeshLocalIid(aMeshLocalIid);
171 if (proxy != nullptr)
172 {
173 TriggerCallback(OT_BACKBONE_ROUTER_NDPROXY_REMOVED, proxy->mAddressIid);
174 Erase(*proxy);
175 }
176 else
177 {
178 proxy = FindInvalid();
179
180 // TODO: evict stale DUA entries to have room for this new DUA.
181 VerifyOrExit(proxy != nullptr, error = kErrorNoBufs);
182 }
183
184 proxy->Init(aAddressIid, aMeshLocalIid, aRloc16, timeSinceLastTransaction);
185 mIsAnyDadInProcess = true;
186
187 exit:
188 otLogInfoBbr("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(),
189 aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error));
190 return error;
191 }
192
FindByAddressIid(const Ip6::InterfaceIdentifier & aAddressIid)193 NdProxyTable::NdProxy *NdProxyTable::FindByAddressIid(const Ip6::InterfaceIdentifier &aAddressIid)
194 {
195 NdProxy *found = nullptr;
196
197 for (NdProxy &proxy : Iterate(kFilterValid))
198 {
199 if (proxy.mAddressIid == aAddressIid)
200 {
201 ExitNow(found = &proxy);
202 }
203 }
204
205 exit:
206 otLogDebgBbr("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(),
207 found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND");
208 return found;
209 }
210
FindByMeshLocalIid(const Ip6::InterfaceIdentifier & aMeshLocalIid)211 NdProxyTable::NdProxy *NdProxyTable::FindByMeshLocalIid(const Ip6::InterfaceIdentifier &aMeshLocalIid)
212 {
213 NdProxy *found = nullptr;
214
215 for (NdProxy &proxy : Iterate(kFilterValid))
216 {
217 if (proxy.mMeshLocalIid == aMeshLocalIid)
218 {
219 ExitNow(found = &proxy);
220 }
221 }
222
223 exit:
224 otLogDebgBbr("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(),
225 found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND");
226 return found;
227 }
228
FindInvalid(void)229 NdProxyTable::NdProxy *NdProxyTable::FindInvalid(void)
230 {
231 NdProxy *found = nullptr;
232
233 for (NdProxy &proxy : Iterate(kFilterInvalid))
234 {
235 ExitNow(found = &proxy);
236 }
237
238 exit:
239 otLogDebgBbr("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND");
240 return found;
241 }
242
HandleTimer(void)243 void NdProxyTable::HandleTimer(void)
244 {
245 VerifyOrExit(mIsAnyDadInProcess);
246
247 mIsAnyDadInProcess = false;
248
249 for (NdProxy &proxy : Iterate(kFilterDadInProcess))
250 {
251 if (proxy.IsDadAttamptsComplete())
252 {
253 proxy.mDadFlag = false;
254 NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false);
255 }
256 else
257 {
258 mIsAnyDadInProcess = true;
259
260 if (Get<BackboneRouter::Manager>().SendBackboneQuery(GetDua(proxy)) == kErrorNone)
261 {
262 proxy.IncreaseDadAttampts();
263 }
264 }
265 }
266
267 exit:
268 return;
269 }
270
SetCallback(otBackboneRouterNdProxyCallback aCallback,void * aContext)271 void NdProxyTable::SetCallback(otBackboneRouterNdProxyCallback aCallback, void *aContext)
272 {
273 mCallback = aCallback;
274 mCallbackContext = aContext;
275 }
276
TriggerCallback(otBackboneRouterNdProxyEvent aEvent,const Ip6::InterfaceIdentifier & aAddressIid) const277 void NdProxyTable::TriggerCallback(otBackboneRouterNdProxyEvent aEvent,
278 const Ip6::InterfaceIdentifier &aAddressIid) const
279 {
280 Ip6::Address dua;
281 const Ip6::Prefix *prefix = Get<BackboneRouter::Leader>().GetDomainPrefix();
282
283 VerifyOrExit(mCallback != nullptr);
284
285 OT_ASSERT(prefix != nullptr);
286
287 dua.SetPrefix(*prefix);
288 dua.SetIid(aAddressIid);
289
290 mCallback(mCallbackContext, aEvent, &dua);
291
292 exit:
293 return;
294 }
295
NotifyDadComplete(NdProxyTable::NdProxy & aNdProxy,bool aDuplicated)296 void NdProxyTable::NotifyDadComplete(NdProxyTable::NdProxy &aNdProxy, bool aDuplicated)
297 {
298 if (aDuplicated)
299 {
300 Erase(aNdProxy);
301 }
302 else
303 {
304 aNdProxy.mDadAttempts = Mle::kDuaDadRepeats;
305 }
306 }
307
GetDua(NdProxy & aNdProxy)308 Ip6::Address NdProxyTable::GetDua(NdProxy &aNdProxy)
309 {
310 Ip6::Address dua;
311 const Ip6::Prefix *domainPrefix = Get<BackboneRouter::Leader>().GetDomainPrefix();
312
313 OT_ASSERT(domainPrefix != nullptr);
314
315 dua.SetPrefix(*domainPrefix);
316 dua.SetIid(aNdProxy.mAddressIid);
317
318 return dua;
319 }
320
ResolveDua(const Ip6::Address & aDua)321 NdProxyTable::NdProxy *NdProxyTable::ResolveDua(const Ip6::Address &aDua)
322 {
323 return Get<Leader>().IsDomainUnicast(aDua) ? FindByAddressIid(aDua.GetIid()) : nullptr;
324 }
325
NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy & aNdProxy,bool aIsRenew)326 void NdProxyTable::NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy &aNdProxy, bool aIsRenew)
327 {
328 if (!aNdProxy.mDadFlag)
329 {
330 TriggerCallback(aIsRenew ? OT_BACKBONE_ROUTER_NDPROXY_RENEWED : OT_BACKBONE_ROUTER_NDPROXY_ADDED,
331 aNdProxy.mAddressIid);
332
333 IgnoreError(Get<BackboneRouter::Manager>().SendProactiveBackboneNotification(
334 GetDua(aNdProxy), aNdProxy.GetMeshLocalIid(), aNdProxy.GetTimeSinceLastTransaction()));
335 }
336 }
337
GetInfo(const Ip6::Address & aDua,otBackboneRouterNdProxyInfo & aNdProxyInfo)338 Error NdProxyTable::GetInfo(const Ip6::Address &aDua, otBackboneRouterNdProxyInfo &aNdProxyInfo)
339 {
340 Error error = kErrorNotFound;
341
342 VerifyOrExit(Get<Leader>().IsDomainUnicast(aDua), error = kErrorInvalidArgs);
343
344 for (NdProxy &proxy : Iterate(kFilterValid))
345 {
346 if (proxy.mAddressIid == aDua.GetIid())
347 {
348 aNdProxyInfo.mMeshLocalIid = &proxy.mMeshLocalIid;
349 aNdProxyInfo.mTimeSinceLastTransaction = proxy.GetTimeSinceLastTransaction();
350 aNdProxyInfo.mRloc16 = proxy.mRloc16;
351
352 ExitNow(error = kErrorNone);
353 }
354 }
355
356 exit:
357 return error;
358 }
359
360 } // namespace BackboneRouter
361
362 } // namespace ot
363
364 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
365