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