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