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