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 #include "common/num_utils.hpp"
42
43 namespace ot {
44
45 namespace BackboneRouter {
46
47 RegisterLogModule("BbrNdProxy");
48
Init(const Ip6::InterfaceIdentifier & aAddressIid,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint16_t aRloc16,uint32_t aTimeSinceLastTransaction)49 void NdProxyTable::NdProxy::Init(const Ip6::InterfaceIdentifier &aAddressIid,
50 const Ip6::InterfaceIdentifier &aMeshLocalIid,
51 uint16_t aRloc16,
52 uint32_t aTimeSinceLastTransaction)
53 {
54 OT_ASSERT(!mValid);
55
56 Clear();
57
58 mValid = true;
59 mAddressIid = aAddressIid;
60 mMeshLocalIid = aMeshLocalIid;
61 mDadFlag = true;
62
63 Update(aRloc16, aTimeSinceLastTransaction);
64 }
65
Update(uint16_t aRloc16,uint32_t aTimeSinceLastTransaction)66 void NdProxyTable::NdProxy::Update(uint16_t aRloc16, uint32_t aTimeSinceLastTransaction)
67 {
68 OT_ASSERT(mValid);
69
70 mRloc16 = aRloc16;
71 aTimeSinceLastTransaction = Min(aTimeSinceLastTransaction, kMaxTimeSinceLastTransaction);
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) { aNdProxy.mValid = false; }
127
HandleDomainPrefixUpdate(DomainPrefixEvent aEvent)128 void NdProxyTable::HandleDomainPrefixUpdate(DomainPrefixEvent aEvent)
129 {
130 if (aEvent == kDomainPrefixAdded || aEvent == kDomainPrefixRemoved || aEvent == kDomainPrefixRefreshed)
131 {
132 Clear();
133 }
134 }
135
Clear(void)136 void NdProxyTable::Clear(void)
137 {
138 for (NdProxy &proxy : mProxies)
139 {
140 proxy.Clear();
141 }
142
143 mCallback.InvokeIfSet(MapEnum(NdProxy::kCleared), nullptr);
144
145 LogInfo("NdProxyTable::Clear!");
146 }
147
Register(const Ip6::InterfaceIdentifier & aAddressIid,const Ip6::InterfaceIdentifier & aMeshLocalIid,uint16_t aRloc16,const uint32_t * aTimeSinceLastTransaction)148 Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid,
149 const Ip6::InterfaceIdentifier &aMeshLocalIid,
150 uint16_t aRloc16,
151 const uint32_t *aTimeSinceLastTransaction)
152 {
153 Error error = kErrorNone;
154 NdProxy *proxy = FindByAddressIid(aAddressIid);
155 uint32_t timeSinceLastTransaction = aTimeSinceLastTransaction == nullptr ? 0 : *aTimeSinceLastTransaction;
156
157 if (proxy != nullptr)
158 {
159 VerifyOrExit(proxy->mMeshLocalIid == aMeshLocalIid, error = kErrorDuplicated);
160
161 proxy->Update(aRloc16, timeSinceLastTransaction);
162 NotifyDuaRegistrationOnBackboneLink(*proxy, /* aIsRenew */ true);
163 ExitNow();
164 }
165
166 proxy = FindByMeshLocalIid(aMeshLocalIid);
167 if (proxy != nullptr)
168 {
169 TriggerCallback(NdProxy::kRemoved, proxy->mAddressIid);
170 Erase(*proxy);
171 }
172 else
173 {
174 proxy = FindInvalid();
175
176 // TODO: evict stale DUA entries to have room for this new DUA.
177 VerifyOrExit(proxy != nullptr, error = kErrorNoBufs);
178 }
179
180 proxy->Init(aAddressIid, aMeshLocalIid, aRloc16, timeSinceLastTransaction);
181 mIsAnyDadInProcess = true;
182
183 exit:
184 LogInfo("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %lu => %s", aAddressIid.ToString().AsCString(),
185 aMeshLocalIid.ToString().AsCString(), aRloc16, ToUlong(timeSinceLastTransaction), ErrorToString(error));
186 return error;
187 }
188
FindByAddressIid(const Ip6::InterfaceIdentifier & aAddressIid)189 NdProxyTable::NdProxy *NdProxyTable::FindByAddressIid(const Ip6::InterfaceIdentifier &aAddressIid)
190 {
191 NdProxy *found = nullptr;
192
193 for (NdProxy &proxy : Iterate(kFilterValid))
194 {
195 if (proxy.mAddressIid == aAddressIid)
196 {
197 ExitNow(found = &proxy);
198 }
199 }
200
201 exit:
202 LogDebg("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(),
203 found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND");
204 return found;
205 }
206
FindByMeshLocalIid(const Ip6::InterfaceIdentifier & aMeshLocalIid)207 NdProxyTable::NdProxy *NdProxyTable::FindByMeshLocalIid(const Ip6::InterfaceIdentifier &aMeshLocalIid)
208 {
209 NdProxy *found = nullptr;
210
211 for (NdProxy &proxy : Iterate(kFilterValid))
212 {
213 if (proxy.mMeshLocalIid == aMeshLocalIid)
214 {
215 ExitNow(found = &proxy);
216 }
217 }
218
219 exit:
220 LogDebg("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(),
221 found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND");
222 return found;
223 }
224
FindInvalid(void)225 NdProxyTable::NdProxy *NdProxyTable::FindInvalid(void)
226 {
227 NdProxy *found = nullptr;
228
229 for (NdProxy &proxy : Iterate(kFilterInvalid))
230 {
231 ExitNow(found = &proxy);
232 }
233
234 exit:
235 LogDebg("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND");
236 return found;
237 }
238
HandleTimer(void)239 void NdProxyTable::HandleTimer(void)
240 {
241 VerifyOrExit(mIsAnyDadInProcess);
242
243 mIsAnyDadInProcess = false;
244
245 for (NdProxy &proxy : Iterate(kFilterDadInProcess))
246 {
247 if (proxy.IsDadAttemptsComplete())
248 {
249 proxy.mDadFlag = false;
250 NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false);
251 }
252 else
253 {
254 mIsAnyDadInProcess = true;
255
256 if (Get<BackboneRouter::Manager>().SendBackboneQuery(GetDua(proxy)) == kErrorNone)
257 {
258 proxy.IncreaseDadAttempts();
259 }
260 }
261 }
262
263 exit:
264 return;
265 }
266
TriggerCallback(NdProxy::Event aEvent,const Ip6::InterfaceIdentifier & aAddressIid) const267 void NdProxyTable::TriggerCallback(NdProxy::Event aEvent, const Ip6::InterfaceIdentifier &aAddressIid) const
268 {
269 Ip6::Address dua;
270 const Ip6::Prefix *prefix = Get<BackboneRouter::Leader>().GetDomainPrefix();
271
272 VerifyOrExit(mCallback.IsSet());
273
274 OT_ASSERT(prefix != nullptr);
275
276 dua.SetPrefix(*prefix);
277 dua.SetIid(aAddressIid);
278
279 mCallback.Invoke(MapEnum(aEvent), &dua);
280
281 exit:
282 return;
283 }
284
NotifyDadComplete(NdProxyTable::NdProxy & aNdProxy,bool aDuplicated)285 void NdProxyTable::NotifyDadComplete(NdProxyTable::NdProxy &aNdProxy, bool aDuplicated)
286 {
287 if (aDuplicated)
288 {
289 Erase(aNdProxy);
290 }
291 else
292 {
293 aNdProxy.mDadAttempts = kDuaDadRepeats;
294 }
295 }
296
GetDua(NdProxy & aNdProxy)297 Ip6::Address NdProxyTable::GetDua(NdProxy &aNdProxy)
298 {
299 Ip6::Address dua;
300 const Ip6::Prefix *domainPrefix = Get<BackboneRouter::Leader>().GetDomainPrefix();
301
302 OT_ASSERT(domainPrefix != nullptr);
303
304 dua.SetPrefix(*domainPrefix);
305 dua.SetIid(aNdProxy.mAddressIid);
306
307 return dua;
308 }
309
ResolveDua(const Ip6::Address & aDua)310 NdProxyTable::NdProxy *NdProxyTable::ResolveDua(const Ip6::Address &aDua)
311 {
312 return Get<Leader>().IsDomainUnicast(aDua) ? FindByAddressIid(aDua.GetIid()) : nullptr;
313 }
314
NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy & aNdProxy,bool aIsRenew)315 void NdProxyTable::NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy &aNdProxy, bool aIsRenew)
316 {
317 if (!aNdProxy.mDadFlag)
318 {
319 TriggerCallback(aIsRenew ? NdProxy::kRenewed : NdProxy::kAdded, aNdProxy.mAddressIid);
320
321 IgnoreError(Get<BackboneRouter::Manager>().SendProactiveBackboneNotification(
322 GetDua(aNdProxy), aNdProxy.GetMeshLocalIid(), aNdProxy.GetTimeSinceLastTransaction()));
323 }
324 }
325
GetInfo(const Ip6::Address & aDua,otBackboneRouterNdProxyInfo & aNdProxyInfo)326 Error NdProxyTable::GetInfo(const Ip6::Address &aDua, otBackboneRouterNdProxyInfo &aNdProxyInfo)
327 {
328 Error error = kErrorNotFound;
329
330 VerifyOrExit(Get<Leader>().IsDomainUnicast(aDua), error = kErrorInvalidArgs);
331
332 for (NdProxy &proxy : Iterate(kFilterValid))
333 {
334 if (proxy.mAddressIid == aDua.GetIid())
335 {
336 aNdProxyInfo.mMeshLocalIid = &proxy.mMeshLocalIid;
337 aNdProxyInfo.mTimeSinceLastTransaction = proxy.GetTimeSinceLastTransaction();
338 aNdProxyInfo.mRloc16 = proxy.mRloc16;
339
340 ExitNow(error = kErrorNone);
341 }
342 }
343
344 exit:
345 return error;
346 }
347
348 } // namespace BackboneRouter
349
350 } // namespace ot
351
352 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
353