• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, 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's EID-to-RLOC mapping and caching.
32  */
33 
34 #include "address_resolver.hpp"
35 
36 #include "instance/instance.hpp"
37 
38 namespace ot {
39 
40 RegisterLogModule("AddrResolver");
41 
AddressResolver(Instance & aInstance)42 AddressResolver::AddressResolver(Instance &aInstance)
43     : InstanceLocator(aInstance)
44 #if OPENTHREAD_FTD
45     , mCacheEntryPool(aInstance)
46     , mIcmpHandler(&AddressResolver::HandleIcmpReceive, this)
47 #endif
48 {
49 #if OPENTHREAD_FTD
50     IgnoreError(Get<Ip6::Icmp>().RegisterHandler(mIcmpHandler));
51 #endif
52 }
53 
54 #if OPENTHREAD_FTD
55 
Clear(void)56 void AddressResolver::Clear(void)
57 {
58     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList};
59 
60     for (CacheEntryList *list : lists)
61     {
62         CacheEntry *entry;
63 
64         while ((entry = list->Pop()) != nullptr)
65         {
66             if (list == &mQueryList)
67             {
68                 Get<MeshForwarder>().HandleResolved(entry->GetTarget(), kErrorDrop);
69             }
70 
71             mCacheEntryPool.Free(*entry);
72         }
73     }
74 }
75 
GetNextCacheEntry(EntryInfo & aInfo,Iterator & aIterator) const76 Error AddressResolver::GetNextCacheEntry(EntryInfo &aInfo, Iterator &aIterator) const
77 {
78     Error                 error = kErrorNone;
79     const CacheEntryList *list  = aIterator.GetList();
80     const CacheEntry     *entry = aIterator.GetEntry();
81 
82     while (entry == nullptr)
83     {
84         if (list == nullptr)
85         {
86             list = &mCachedList;
87         }
88         else if (list == &mCachedList)
89         {
90             list = &mSnoopedList;
91         }
92         else if (list == &mSnoopedList)
93         {
94             list = &mQueryList;
95         }
96         else if (list == &mQueryList)
97         {
98             list = &mQueryRetryList;
99         }
100         else
101         {
102             ExitNow(error = kErrorNotFound);
103         }
104 
105         entry = list->GetHead();
106     }
107 
108     // Update the iterator then populate the `aInfo`.
109 
110     aIterator.SetEntry(entry->GetNext());
111     aIterator.SetList(list);
112 
113     aInfo.Clear();
114     aInfo.mTarget = entry->GetTarget();
115     aInfo.mRloc16 = entry->GetRloc16();
116 
117     if (list == &mCachedList)
118     {
119         aInfo.mState          = MapEnum(EntryInfo::kStateCached);
120         aInfo.mCanEvict       = true;
121         aInfo.mValidLastTrans = entry->IsLastTransactionTimeValid();
122 
123         VerifyOrExit(entry->IsLastTransactionTimeValid());
124 
125         aInfo.mLastTransTime = entry->GetLastTransactionTime();
126         AsCoreType(&aInfo.mMeshLocalEid).SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix());
127         AsCoreType(&aInfo.mMeshLocalEid).SetIid(entry->GetMeshLocalIid());
128 
129         ExitNow();
130     }
131 
132     if (list == &mSnoopedList)
133     {
134         aInfo.mState = MapEnum(EntryInfo::kStateSnooped);
135     }
136     else if (list == &mQueryList)
137     {
138         aInfo.mState = MapEnum(EntryInfo::kStateQuery);
139     }
140     else
141     {
142         aInfo.mState    = MapEnum(EntryInfo::kStateRetryQuery);
143         aInfo.mRampDown = entry->IsInRampDown();
144     }
145 
146     aInfo.mCanEvict   = entry->CanEvict();
147     aInfo.mTimeout    = entry->GetTimeout();
148     aInfo.mRetryDelay = entry->GetRetryDelay();
149 
150 exit:
151     return error;
152 }
153 
RemoveEntriesForRouterId(uint8_t aRouterId)154 void AddressResolver::RemoveEntriesForRouterId(uint8_t aRouterId)
155 {
156     Remove(Mle::Rloc16FromRouterId(aRouterId), /* aMatchRouterId */ true);
157 }
158 
RemoveEntriesForRloc16(uint16_t aRloc16)159 void AddressResolver::RemoveEntriesForRloc16(uint16_t aRloc16) { Remove(aRloc16, /* aMatchRouterId */ false); }
160 
GetEntryAfter(CacheEntry * aPrev,CacheEntryList & aList)161 AddressResolver::CacheEntry *AddressResolver::GetEntryAfter(CacheEntry *aPrev, CacheEntryList &aList)
162 {
163     return (aPrev == nullptr) ? aList.GetHead() : aPrev->GetNext();
164 }
165 
Remove(uint16_t aRloc16,bool aMatchRouterId)166 void AddressResolver::Remove(uint16_t aRloc16, bool aMatchRouterId)
167 {
168     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList};
169 
170     for (CacheEntryList *list : lists)
171     {
172         CacheEntry *prev = nullptr;
173         CacheEntry *entry;
174 
175         while ((entry = GetEntryAfter(prev, *list)) != nullptr)
176         {
177             if ((aMatchRouterId && Mle::RouterIdMatch(entry->GetRloc16(), aRloc16)) ||
178                 (!aMatchRouterId && (entry->GetRloc16() == aRloc16)))
179             {
180                 RemoveCacheEntry(*entry, *list, prev, aMatchRouterId ? kReasonRemovingRouterId : kReasonRemovingRloc16);
181                 mCacheEntryPool.Free(*entry);
182 
183                 // If the entry is removed from list, we keep the same
184                 // `prev` pointer.
185             }
186             else
187             {
188                 prev = entry;
189             }
190         }
191     }
192 }
193 
FindCacheEntry(const Ip6::Address & aEid,CacheEntryList * & aList,CacheEntry * & aPrevEntry)194 AddressResolver::CacheEntry *AddressResolver::FindCacheEntry(const Ip6::Address &aEid,
195                                                              CacheEntryList    *&aList,
196                                                              CacheEntry        *&aPrevEntry)
197 {
198     CacheEntry     *entry   = nullptr;
199     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList};
200 
201     for (CacheEntryList *list : lists)
202     {
203         aList = list;
204         entry = aList->FindMatchingWithPrev(aPrevEntry, aEid);
205         VerifyOrExit(entry == nullptr);
206     }
207 
208 exit:
209     return entry;
210 }
211 
RemoveEntryForAddress(const Ip6::Address & aEid)212 void AddressResolver::RemoveEntryForAddress(const Ip6::Address &aEid) { Remove(aEid, kReasonRemovingEid); }
213 
Remove(const Ip6::Address & aEid,Reason aReason)214 void AddressResolver::Remove(const Ip6::Address &aEid, Reason aReason)
215 {
216     CacheEntry     *entry;
217     CacheEntry     *prev;
218     CacheEntryList *list;
219 
220     entry = FindCacheEntry(aEid, list, prev);
221     VerifyOrExit(entry != nullptr);
222 
223     RemoveCacheEntry(*entry, *list, prev, aReason);
224     mCacheEntryPool.Free(*entry);
225 
226 exit:
227     return;
228 }
229 
ReplaceEntriesForRloc16(uint16_t aOldRloc16,uint16_t aNewRloc16)230 void AddressResolver::ReplaceEntriesForRloc16(uint16_t aOldRloc16, uint16_t aNewRloc16)
231 {
232     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList};
233 
234     for (CacheEntryList *list : lists)
235     {
236         for (CacheEntry &entry : *list)
237         {
238             if (entry.GetRloc16() == aOldRloc16)
239             {
240                 entry.SetRloc16(aNewRloc16);
241             }
242         }
243     }
244 }
245 
NewCacheEntry(bool aSnoopedEntry)246 AddressResolver::CacheEntry *AddressResolver::NewCacheEntry(bool aSnoopedEntry)
247 {
248     CacheEntry     *newEntry  = nullptr;
249     CacheEntry     *prevEntry = nullptr;
250     CacheEntryList *lists[]   = {&mSnoopedList, &mQueryRetryList, &mQueryList, &mCachedList};
251 
252     // The following order is used when trying to allocate a new cache
253     // entry: First the cache pool is checked, followed by the list
254     // of snooped entries, then query-retry list (entries in delay
255     // retry timeout wait due to a prior query failing to get a
256     // response), then the query list (entries actively querying and
257     // waiting for address notification response), and finally the
258     // cached (in-use) list. Within each list the oldest entry is
259     // reclaimed first (the list's tail). We also make sure the entry
260     // can be evicted (e.g., first time query entries can not be
261     // evicted till timeout).
262 
263     newEntry = mCacheEntryPool.Allocate();
264     VerifyOrExit(newEntry == nullptr);
265 
266     for (CacheEntryList *list : lists)
267     {
268         CacheEntry *prev;
269         CacheEntry *entry;
270         uint16_t    numNonEvictable = 0;
271 
272         for (prev = nullptr; (entry = GetEntryAfter(prev, *list)) != nullptr; prev = entry)
273         {
274             if ((list != &mCachedList) && !entry->CanEvict())
275             {
276                 numNonEvictable++;
277                 continue;
278             }
279 
280             newEntry  = entry;
281             prevEntry = prev;
282         }
283 
284         if (newEntry != nullptr)
285         {
286             RemoveCacheEntry(*newEntry, *list, prevEntry, kReasonEvictingForNewEntry);
287             ExitNow();
288         }
289 
290         if (aSnoopedEntry && (list == &mSnoopedList))
291         {
292             // Check if the new entry is being requested for "snoop
293             // optimization" (i.e., inspection of a received message).
294             // When a new snooped entry is added, we do not allow it
295             // to be evicted for a short timeout. This allows some
296             // delay for a response message to use the entry (if entry
297             // is used it will be moved to the cached list). If a
298             // snooped entry is not used after the timeout, we allow
299             // it to be evicted. To ensure snooped entries do not
300             // overwrite other cached entries, we limit the number of
301             // snooped entries that are in timeout mode and cannot be
302             // evicted by `kMaxNonEvictableSnoopedEntries`.
303 
304             VerifyOrExit(numNonEvictable < kMaxNonEvictableSnoopedEntries);
305         }
306     }
307 
308 exit:
309     return newEntry;
310 }
311 
RemoveCacheEntry(CacheEntry & aEntry,CacheEntryList & aList,CacheEntry * aPrevEntry,Reason aReason)312 void AddressResolver::RemoveCacheEntry(CacheEntry     &aEntry,
313                                        CacheEntryList &aList,
314                                        CacheEntry     *aPrevEntry,
315                                        Reason          aReason)
316 {
317     aList.PopAfter(aPrevEntry);
318 
319     if (&aList == &mQueryList)
320     {
321         Get<MeshForwarder>().HandleResolved(aEntry.GetTarget(), kErrorDrop);
322     }
323 
324     LogCacheEntryChange(kEntryRemoved, aReason, aEntry, &aList);
325 }
326 
UpdateCacheEntry(const Ip6::Address & aEid,uint16_t aRloc16)327 Error AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, uint16_t aRloc16)
328 {
329     // This method updates an existing cache entry for the EID (if any).
330     // Returns `kErrorNone` if entry is found and successfully updated,
331     // `kErrorNotFound` if no matching entry.
332 
333     Error           error = kErrorNone;
334     CacheEntryList *list;
335     CacheEntry     *entry;
336     CacheEntry     *prev;
337 
338     entry = FindCacheEntry(aEid, list, prev);
339     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
340 
341     if ((list == &mCachedList) || (list == &mSnoopedList))
342     {
343         VerifyOrExit(entry->GetRloc16() != aRloc16);
344         entry->SetRloc16(aRloc16);
345     }
346     else
347     {
348         // Entry is in `mQueryList` or `mQueryRetryList`. Remove it
349         // from its current list, update it, and then add it to the
350         // `mCachedList`.
351 
352         list->PopAfter(prev);
353 
354         entry->SetRloc16(aRloc16);
355         entry->MarkLastTransactionTimeAsInvalid();
356         mCachedList.Push(*entry);
357 
358         Get<MeshForwarder>().HandleResolved(aEid, kErrorNone);
359     }
360 
361     LogCacheEntryChange(kEntryUpdated, kReasonSnoop, *entry);
362 
363 exit:
364     return error;
365 }
366 
UpdateSnoopedCacheEntry(const Ip6::Address & aEid,uint16_t aRloc16,uint16_t aDest)367 void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, uint16_t aRloc16, uint16_t aDest)
368 {
369     uint16_t    numNonEvictable = 0;
370     CacheEntry *entry;
371 
372     VerifyOrExit(Get<Mle::MleRouter>().IsFullThreadDevice());
373 
374 #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES
375     {
376         uint16_t rloc16;
377 
378         VerifyOrExit(ResolveUsingNetDataServices(aEid, rloc16) != kErrorNone);
379     }
380 #endif
381 
382     VerifyOrExit(UpdateCacheEntry(aEid, aRloc16) != kErrorNone);
383 
384     // Skip if the `aRloc16` (i.e., the source of the snooped message)
385     // is this device or an MTD (minimal) child of the device itself.
386 
387     VerifyOrExit(!Get<Mle::Mle>().HasRloc16(aRloc16) && !Get<ChildTable>().HasMinimalChild(aRloc16));
388 
389     // Ensure that the destination of the snooped message is this device
390     // or a minimal child of this device.
391 
392     VerifyOrExit(Get<Mle::Mle>().HasRloc16(aDest) || Get<ChildTable>().HasMinimalChild(aDest));
393 
394     entry = NewCacheEntry(/* aSnoopedEntry */ true);
395     VerifyOrExit(entry != nullptr);
396 
397     for (CacheEntry &snooped : mSnoopedList)
398     {
399         if (!snooped.CanEvict())
400         {
401             numNonEvictable++;
402         }
403     }
404 
405     entry->SetTarget(aEid);
406     entry->SetRloc16(aRloc16);
407 
408     if (numNonEvictable < kMaxNonEvictableSnoopedEntries)
409     {
410         entry->SetCanEvict(false);
411         entry->SetTimeout(kSnoopBlockEvictionTimeout);
412 
413         Get<TimeTicker>().RegisterReceiver(TimeTicker::kAddressResolver);
414     }
415     else
416     {
417         entry->SetCanEvict(true);
418         entry->SetTimeout(0);
419     }
420 
421     mSnoopedList.Push(*entry);
422 
423     LogCacheEntryChange(kEntryAdded, kReasonSnoop, *entry);
424 
425 exit:
426     return;
427 }
428 
RestartAddressQueries(void)429 void AddressResolver::RestartAddressQueries(void)
430 {
431     CacheEntry *tail;
432 
433     // We move all entries from `mQueryRetryList` at the tail of
434     // `mQueryList` and then (re)send Address Query for all entries in
435     // the updated `mQueryList`.
436 
437     tail = mQueryList.GetTail();
438 
439     if (tail == nullptr)
440     {
441         mQueryList.SetHead(mQueryRetryList.GetHead());
442     }
443     else
444     {
445         tail->SetNext(mQueryRetryList.GetHead());
446     }
447 
448     mQueryRetryList.Clear();
449 
450     for (CacheEntry &entry : mQueryList)
451     {
452         IgnoreError(SendAddressQuery(entry.GetTarget()));
453 
454         entry.SetTimeout(kAddressQueryTimeout);
455         entry.SetRetryDelay(kAddressQueryInitialRetryDelay);
456         entry.SetCanEvict(false);
457     }
458 }
459 
LookUp(const Ip6::Address & aEid)460 uint16_t AddressResolver::LookUp(const Ip6::Address &aEid)
461 {
462     uint16_t rloc16 = Mle::kInvalidRloc16;
463 
464     IgnoreError(Resolve(aEid, rloc16, /* aAllowAddressQuery */ false));
465     return rloc16;
466 }
467 
Resolve(const Ip6::Address & aEid,uint16_t & aRloc16,bool aAllowAddressQuery)468 Error AddressResolver::Resolve(const Ip6::Address &aEid, uint16_t &aRloc16, bool aAllowAddressQuery)
469 {
470     Error           error = kErrorNone;
471     CacheEntry     *entry;
472     CacheEntry     *prev = nullptr;
473     CacheEntryList *list;
474 
475 #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES
476     VerifyOrExit(ResolveUsingNetDataServices(aEid, aRloc16) != kErrorNone);
477 #endif
478 
479     entry = FindCacheEntry(aEid, list, prev);
480 
481     if ((entry != nullptr) && ((list == &mCachedList) || (list == &mSnoopedList)))
482     {
483         bool isFresh;
484 
485         list->PopAfter(prev);
486 
487         // If the `entry->GetRloc16()` is unreachable (there is no
488         // valid next hop towards it), it may be a stale entry. We
489         // clear the entry to allow new address query to be sent for
490         // it, unless the entry has been recently updated, i.e., we
491         // have recently received an `AddressNotify` for it and its
492         // `FreshnessTimeout` has not expired yet.
493         //
494         // The `FreshnessTimeout` check prevents repeated address
495         // query transmissions when mesh routes are not yet
496         // discovered (e.g., after initial attach) or if there is a
497         // temporary link issue.
498 
499         isFresh = (list == &mCachedList) && !entry->IsFreshnessTimeoutZero();
500 
501         if (!isFresh && (Get<RouterTable>().GetNextHop(entry->GetRloc16()) == Mle::kInvalidRloc16))
502         {
503             mCacheEntryPool.Free(*entry);
504             entry = nullptr;
505         }
506 
507         if (entry != nullptr)
508         {
509             // Push the entry at the head of cached list.
510 
511             if (list == &mSnoopedList)
512             {
513                 entry->MarkLastTransactionTimeAsInvalid();
514             }
515 
516             mCachedList.Push(*entry);
517             aRloc16 = entry->GetRloc16();
518             ExitNow();
519         }
520     }
521 
522     if (entry == nullptr)
523     {
524         // If the entry is not present in any of the lists, try to
525         // allocate a new entry and perform address query. We do not
526         // allow first-time address query entries to be evicted till
527         // timeout.
528 
529         VerifyOrExit(aAllowAddressQuery, error = kErrorNotFound);
530 
531         entry = NewCacheEntry(/* aSnoopedEntry */ false);
532         VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
533 
534         entry->SetTarget(aEid);
535         entry->SetRloc16(Mle::kInvalidRloc16);
536         entry->SetRetryDelay(kAddressQueryInitialRetryDelay);
537         entry->SetCanEvict(false);
538         list = nullptr;
539     }
540 
541     // Note that if `aAllowAddressQuery` is `false` then the `entry`
542     // is definitely already in a list, i.e., we cannot not get here
543     // with `aAllowAddressQuery` being `false` and `entry` being a
544     // newly allocated one, due to the `VerifyOrExit` check that
545     // `aAllowAddressQuery` is `true` before allocating a new cache
546     // entry.
547     VerifyOrExit(aAllowAddressQuery, error = kErrorNotFound);
548 
549     if (list == &mQueryList)
550     {
551         ExitNow(error = kErrorAddressQuery);
552     }
553 
554     if (list == &mQueryRetryList)
555     {
556         // Allow an entry in query-retry mode to resend an Address
557         // Query again only if it is in ramp down mode, i.e., the
558         // retry delay timeout is expired.
559 
560         VerifyOrExit(entry->IsInRampDown(), error = kErrorDrop);
561         mQueryRetryList.PopAfter(prev);
562     }
563 
564     entry->SetTimeout(kAddressQueryTimeout);
565 
566     error = SendAddressQuery(aEid);
567     VerifyOrExit(error == kErrorNone, mCacheEntryPool.Free(*entry));
568 
569     if (list == nullptr)
570     {
571         LogCacheEntryChange(kEntryAdded, kReasonQueryRequest, *entry);
572     }
573 
574     mQueryList.Push(*entry);
575     error = kErrorAddressQuery;
576 
577 exit:
578     return error;
579 }
580 
581 #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES
582 
ResolveUsingNetDataServices(const Ip6::Address & aEid,uint16_t & aRloc16)583 Error AddressResolver::ResolveUsingNetDataServices(const Ip6::Address &aEid, uint16_t &aRloc16)
584 {
585     // Tries to resolve `aEid` Network Data DNS/SRP Unicast address
586     // service entries.  Returns `kErrorNone` and updates `aRloc16`
587     // if successful, otherwise returns `kErrorNotFound`.
588 
589     Error                                   error = kErrorNotFound;
590     NetworkData::Service::Manager::Iterator iterator;
591     NetworkData::Service::DnsSrpUnicastInfo unicastInfo;
592     NetworkData::Service::DnsSrpUnicastType type = NetworkData::Service::kAddrInServerData;
593 
594     VerifyOrExit(Get<Mle::Mle>().GetDeviceMode().GetNetworkDataType() == NetworkData::kFullSet);
595 
596     while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, type, unicastInfo) == kErrorNone)
597     {
598         if (aEid == unicastInfo.mSockAddr.GetAddress())
599         {
600             aRloc16 = unicastInfo.mRloc16;
601             error   = kErrorNone;
602             ExitNow();
603         }
604     }
605 
606 exit:
607     return error;
608 }
609 
610 #endif // OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES
611 
SendAddressQuery(const Ip6::Address & aEid)612 Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid)
613 {
614     Error            error;
615     Coap::Message   *message;
616     Tmf::MessageInfo messageInfo(GetInstance());
617 
618     message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(kUriAddressQuery);
619     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
620 
621     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aEid));
622 
623     messageInfo.SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast();
624 
625     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
626 
627     LogInfo("Sent %s for %s", UriToString<kUriAddressQuery>(), aEid.ToString().AsCString());
628 
629 exit:
630 
631     Get<TimeTicker>().RegisterReceiver(TimeTicker::kAddressResolver);
632     FreeMessageOnError(message, error);
633 
634 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
635     if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(aEid))
636     {
637         uint16_t selfRloc16 = Get<Mle::MleRouter>().GetRloc16();
638 
639         LogInfo("Extending %s to %s for target %s, rloc16=%04x(self)", UriToString<kUriAddressQuery>(),
640                 UriToString<kUriBackboneQuery>(), aEid.ToString().AsCString(), selfRloc16);
641         IgnoreError(Get<BackboneRouter::Manager>().SendBackboneQuery(aEid, selfRloc16));
642     }
643 #endif
644 
645     return error;
646 }
647 
648 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)649 void AddressResolver::HandleTmf<kUriAddressNotify>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
650 {
651     Ip6::Address             target;
652     Ip6::InterfaceIdentifier meshLocalIid;
653     uint16_t                 rloc16;
654     uint32_t                 lastTransactionTime;
655     CacheEntryList          *list;
656     CacheEntry              *entry;
657     CacheEntry              *prev;
658 
659     VerifyOrExit(aMessage.IsConfirmablePostRequest());
660 
661     SuccessOrExit(Tlv::Find<ThreadTargetTlv>(aMessage, target));
662     SuccessOrExit(Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
663     SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16));
664 
665     switch (Tlv::Find<ThreadLastTransactionTimeTlv>(aMessage, lastTransactionTime))
666     {
667     case kErrorNone:
668         break;
669     case kErrorNotFound:
670         lastTransactionTime = 0;
671         break;
672     default:
673         ExitNow();
674     }
675 
676     LogInfo("Received %s from 0x%04x for %s to 0x%04x", UriToString<kUriAddressNotify>(),
677             aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString(), rloc16);
678 
679     entry = FindCacheEntry(target, list, prev);
680     VerifyOrExit(entry != nullptr);
681 
682     if (list == &mCachedList)
683     {
684         if (entry->IsLastTransactionTimeValid())
685         {
686             // Receiving multiple Address Notification for an EID from
687             // different mesh-local IIDs indicates address is in use
688             // by more than one device. Try to resolve the duplicate
689             // address by sending an Address Error message.
690 
691             VerifyOrExit(entry->GetMeshLocalIid() == meshLocalIid, SendAddressError(target, meshLocalIid, nullptr));
692 
693             VerifyOrExit(lastTransactionTime < entry->GetLastTransactionTime());
694         }
695     }
696 
697     entry->SetRloc16(rloc16);
698     entry->SetMeshLocalIid(meshLocalIid);
699     entry->SetLastTransactionTime(lastTransactionTime);
700     entry->ResetFreshnessTimeout();
701     Get<TimeTicker>().RegisterReceiver(TimeTicker::kAddressResolver);
702 
703     list->PopAfter(prev);
704     mCachedList.Push(*entry);
705 
706     LogCacheEntryChange(kEntryUpdated, kReasonReceivedNotification, *entry);
707 
708     if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
709     {
710         LogInfo("Sent %s ack", UriToString<kUriAddressNotify>());
711     }
712 
713     Get<MeshForwarder>().HandleResolved(target, kErrorNone);
714 
715 exit:
716     return;
717 }
718 
SendAddressError(const Ip6::Address & aTarget,const Ip6::InterfaceIdentifier & aMeshLocalIid,const Ip6::Address * aDestination)719 void AddressResolver::SendAddressError(const Ip6::Address             &aTarget,
720                                        const Ip6::InterfaceIdentifier &aMeshLocalIid,
721                                        const Ip6::Address             *aDestination)
722 {
723     Error            error;
724     Coap::Message   *message;
725     Tmf::MessageInfo messageInfo(GetInstance());
726 
727     VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
728 
729     message->Init(aDestination == nullptr ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost);
730     SuccessOrExit(error = message->AppendUriPathOptions(PathForUri(kUriAddressError)));
731     SuccessOrExit(error = message->SetPayloadMarker());
732 
733     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aTarget));
734     SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
735 
736     if (aDestination == nullptr)
737     {
738         messageInfo.SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast();
739     }
740     else
741     {
742         messageInfo.SetSockAddrToRlocPeerAddrTo(*aDestination);
743     }
744 
745     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
746 
747     LogInfo("Sent %s for target %s", UriToString<kUriAddressError>(), aTarget.ToString().AsCString());
748 
749 exit:
750 
751     if (error != kErrorNone)
752     {
753         FreeMessage(message);
754         LogInfo("Failed to send %s: %s", UriToString<kUriAddressError>(), ErrorToString(error));
755     }
756 }
757 
758 #endif // OPENTHREAD_FTD
759 
760 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)761 void AddressResolver::HandleTmf<kUriAddressError>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
762 {
763     Error                    error = kErrorNone;
764     Ip6::Address             target;
765     Ip6::InterfaceIdentifier meshLocalIid;
766 #if OPENTHREAD_FTD
767     Mac::ExtAddress extAddr;
768     Ip6::Address    destination;
769 #endif
770 
771     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
772 
773     LogInfo("Received %s", UriToString<kUriAddressError>());
774 
775     if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
776     {
777         if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
778         {
779             LogInfo("Sent %s ack", UriToString<kUriAddressError>());
780         }
781     }
782 
783     SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
784     SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
785 
786     for (Ip6::Netif::UnicastAddress &address : Get<ThreadNetif>().GetUnicastAddresses())
787     {
788         if (address.GetAddress() == target && Get<Mle::MleRouter>().GetMeshLocalEid().GetIid() != meshLocalIid)
789         {
790             // Target EID matches address and Mesh Local EID differs
791 #if OPENTHREAD_CONFIG_DUA_ENABLE
792             if (Get<BackboneRouter::Leader>().IsDomainUnicast(address.GetAddress()))
793             {
794                 Get<DuaManager>().NotifyDuplicateDomainUnicastAddress();
795             }
796             else
797 #endif
798             {
799                 Get<ThreadNetif>().RemoveUnicastAddress(address);
800             }
801 
802             ExitNow();
803         }
804     }
805 
806 #if OPENTHREAD_FTD
807     meshLocalIid.ConvertToExtAddress(extAddr);
808 
809     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
810     {
811         if (child.IsFullThreadDevice())
812         {
813             continue;
814         }
815 
816         if (child.GetExtAddress() != extAddr)
817         {
818             // Mesh Local EID differs, so check whether Target EID
819             // matches a child address and if so remove it.
820 
821             if (child.RemoveIp6Address(target) == kErrorNone)
822             {
823                 destination.SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(), child.GetRloc16());
824 
825                 SendAddressError(target, meshLocalIid, &destination);
826                 ExitNow();
827             }
828         }
829     }
830 #endif // OPENTHREAD_FTD
831 
832 exit:
833 
834     if (error != kErrorNone)
835     {
836         LogWarn("Error %s when processing %s", ErrorToString(error), UriToString<kUriAddressError>());
837     }
838 }
839 
840 #if OPENTHREAD_FTD
841 
842 template <>
HandleTmf(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)843 void AddressResolver::HandleTmf<kUriAddressQuery>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
844 {
845     Ip6::Address target;
846     uint32_t     lastTransactionTime;
847 
848     VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
849 
850     SuccessOrExit(Tlv::Find<ThreadTargetTlv>(aMessage, target));
851 
852     LogInfo("Received %s from 0x%04x for target %s", UriToString<kUriAddressQuery>(),
853             aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString());
854 
855     if (Get<ThreadNetif>().HasUnicastAddress(target))
856     {
857         SendAddressQueryResponse(target, Get<Mle::MleRouter>().GetMeshLocalEid().GetIid(), nullptr,
858                                  aMessageInfo.GetPeerAddr());
859         ExitNow();
860     }
861 
862     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
863     {
864         if (child.IsFullThreadDevice() || child.GetLinkFailures() >= Mle::kFailedChildTransmissions)
865         {
866             continue;
867         }
868 
869         if (child.HasIp6Address(target))
870         {
871             lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child.GetLastHeard());
872             SendAddressQueryResponse(target, child.GetMeshLocalIid(), &lastTransactionTime, aMessageInfo.GetPeerAddr());
873             ExitNow();
874         }
875     }
876 
877 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
878     if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(target))
879     {
880         uint16_t srcRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
881 
882         LogInfo("Extending %s to %s for target %s rloc16=%04x", UriToString<kUriAddressQuery>(),
883                 UriToString<kUriBackboneQuery>(), target.ToString().AsCString(), srcRloc16);
884         IgnoreError(Get<BackboneRouter::Manager>().SendBackboneQuery(target, srcRloc16));
885     }
886 #endif
887 
888 exit:
889     return;
890 }
891 
SendAddressQueryResponse(const Ip6::Address & aTarget,const Ip6::InterfaceIdentifier & aMeshLocalIid,const uint32_t * aLastTransactionTime,const Ip6::Address & aDestination)892 void AddressResolver::SendAddressQueryResponse(const Ip6::Address             &aTarget,
893                                                const Ip6::InterfaceIdentifier &aMeshLocalIid,
894                                                const uint32_t                 *aLastTransactionTime,
895                                                const Ip6::Address             &aDestination)
896 {
897     Error            error;
898     Coap::Message   *message;
899     Tmf::MessageInfo messageInfo(GetInstance());
900 
901     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriAddressNotify);
902     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
903 
904     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aTarget));
905     SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
906     SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, Get<Mle::MleRouter>().GetRloc16()));
907 
908     if (aLastTransactionTime != nullptr)
909     {
910         SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, *aLastTransactionTime));
911     }
912 
913     messageInfo.SetSockAddrToRlocPeerAddrTo(aDestination);
914 
915     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
916 
917     LogInfo("Sent %s for target %s", UriToString<kUriAddressNotify>(), aTarget.ToString().AsCString());
918 
919 exit:
920     FreeMessageOnError(message, error);
921 }
922 
HandleTimeTick(void)923 void AddressResolver::HandleTimeTick(void)
924 {
925     bool continueRxingTicks = false;
926 
927     for (CacheEntry &entry : mCachedList)
928     {
929         if (!entry.IsFreshnessTimeoutZero())
930         {
931             entry.DecrementFreshnessTimeout();
932             continueRxingTicks = true;
933         }
934     }
935 
936     for (CacheEntry &entry : mSnoopedList)
937     {
938         if (entry.IsTimeoutZero())
939         {
940             continue;
941         }
942 
943         continueRxingTicks = true;
944         entry.DecrementTimeout();
945 
946         if (entry.IsTimeoutZero())
947         {
948             entry.SetCanEvict(true);
949         }
950     }
951 
952     for (CacheEntry &entry : mQueryRetryList)
953     {
954         if (entry.IsTimeoutZero())
955         {
956             continue;
957         }
958 
959         continueRxingTicks = true;
960         entry.DecrementTimeout();
961 
962         if (entry.IsTimeoutZero())
963         {
964             if (!entry.IsInRampDown())
965             {
966                 entry.SetRampDown(true);
967                 entry.SetTimeout(kAddressQueryMaxRetryDelay);
968 
969                 LogInfo("Starting ramp down of %s retry-delay:%u", entry.GetTarget().ToString().AsCString(),
970                         entry.GetTimeout());
971             }
972             else
973             {
974                 uint16_t retryDelay = entry.GetRetryDelay();
975 
976                 retryDelay >>= 1;
977                 retryDelay = Max(retryDelay, kAddressQueryInitialRetryDelay);
978 
979                 if (retryDelay != entry.GetRetryDelay())
980                 {
981                     entry.SetRetryDelay(retryDelay);
982                     entry.SetTimeout(kAddressQueryMaxRetryDelay);
983 
984                     LogInfo("Ramping down %s retry-delay:%u", entry.GetTarget().ToString().AsCString(), retryDelay);
985                 }
986             }
987         }
988     }
989 
990     {
991         CacheEntry *prev = nullptr;
992         CacheEntry *entry;
993 
994         while ((entry = GetEntryAfter(prev, mQueryList)) != nullptr)
995         {
996             OT_ASSERT(!entry->IsTimeoutZero());
997 
998             continueRxingTicks = true;
999             entry->DecrementTimeout();
1000 
1001             if (entry->IsTimeoutZero())
1002             {
1003                 uint16_t retryDelay = entry->GetRetryDelay();
1004 
1005                 entry->SetTimeout(retryDelay);
1006 
1007                 retryDelay <<= 1;
1008                 retryDelay = Min(retryDelay, kAddressQueryMaxRetryDelay);
1009 
1010                 entry->SetRetryDelay(retryDelay);
1011                 entry->SetCanEvict(true);
1012                 entry->SetRampDown(false);
1013 
1014                 // Move the entry from `mQueryList` to `mQueryRetryList`
1015                 mQueryList.PopAfter(prev);
1016                 mQueryRetryList.Push(*entry);
1017 
1018                 LogInfo("Timed out waiting for %s for %s, retry: %d", UriToString<kUriAddressNotify>(),
1019                         entry->GetTarget().ToString().AsCString(), entry->GetTimeout());
1020 
1021                 Get<MeshForwarder>().HandleResolved(entry->GetTarget(), kErrorDrop);
1022 
1023                 // When the entry is removed from `mQueryList`
1024                 // we keep the `prev` pointer same as before.
1025             }
1026             else
1027             {
1028                 prev = entry;
1029             }
1030         }
1031     }
1032 
1033     if (!continueRxingTicks)
1034     {
1035         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kAddressResolver);
1036     }
1037 }
1038 
HandleIcmpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,const otIcmp6Header * aIcmpHeader)1039 void AddressResolver::HandleIcmpReceive(void                *aContext,
1040                                         otMessage           *aMessage,
1041                                         const otMessageInfo *aMessageInfo,
1042                                         const otIcmp6Header *aIcmpHeader)
1043 {
1044     OT_UNUSED_VARIABLE(aMessageInfo);
1045 
1046     static_cast<AddressResolver *>(aContext)->HandleIcmpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo),
1047                                                                 AsCoreType(aIcmpHeader));
1048 }
1049 
HandleIcmpReceive(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Ip6::Icmp::Header & aIcmpHeader)1050 void AddressResolver::HandleIcmpReceive(Message                 &aMessage,
1051                                         const Ip6::MessageInfo  &aMessageInfo,
1052                                         const Ip6::Icmp::Header &aIcmpHeader)
1053 {
1054     OT_UNUSED_VARIABLE(aMessageInfo);
1055 
1056     Ip6::Header ip6Header;
1057 
1058     VerifyOrExit(aIcmpHeader.GetType() == Ip6::Icmp::Header::kTypeDstUnreach);
1059     VerifyOrExit(aIcmpHeader.GetCode() == Ip6::Icmp::Header::kCodeDstUnreachNoRoute);
1060     SuccessOrExit(aMessage.Read(aMessage.GetOffset(), ip6Header));
1061 
1062     Remove(ip6Header.GetDestination(), kReasonReceivedIcmpDstUnreachNoRoute);
1063 
1064 exit:
1065     return;
1066 }
1067 
1068 // LCOV_EXCL_START
1069 
1070 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1071 
LogCacheEntryChange(EntryChange aChange,Reason aReason,const CacheEntry & aEntry,CacheEntryList * aList)1072 void AddressResolver::LogCacheEntryChange(EntryChange       aChange,
1073                                           Reason            aReason,
1074                                           const CacheEntry &aEntry,
1075                                           CacheEntryList   *aList)
1076 {
1077     static const char *const kChangeStrings[] = {
1078         "added",   // (0) kEntryAdded
1079         "updated", // (1) kEntryUpdated
1080         "removed", // (2) kEntryRemoved
1081     };
1082 
1083     static const char *const kReasonStrings[] = {
1084         "query request",          // (0) kReasonQueryRequest
1085         "snoop",                  // (1) kReasonSnoop
1086         "rx notification",        // (2) kReasonReceivedNotification
1087         "removing router id",     // (3) kReasonRemovingRouterId
1088         "removing rloc16",        // (4) kReasonRemovingRloc16
1089         "rx icmp no route",       // (5) kReasonReceivedIcmpDstUnreachNoRoute
1090         "evicting for new entry", // (6) kReasonEvictingForNewEntry
1091         "removing eid",           // (7) kReasonRemovingEid
1092     };
1093 
1094     struct ChangeEnumCheck
1095     {
1096         InitEnumValidatorCounter();
1097         ValidateNextEnum(kEntryAdded);
1098         ValidateNextEnum(kEntryUpdated);
1099         ValidateNextEnum(kEntryRemoved);
1100     };
1101 
1102     struct ReasonEnumCheck
1103     {
1104         InitEnumValidatorCounter();
1105         ValidateNextEnum(kReasonQueryRequest);
1106         ValidateNextEnum(kReasonSnoop);
1107         ValidateNextEnum(kReasonReceivedNotification);
1108         ValidateNextEnum(kReasonRemovingRouterId);
1109         ValidateNextEnum(kReasonRemovingRloc16);
1110         ValidateNextEnum(kReasonReceivedIcmpDstUnreachNoRoute);
1111         ValidateNextEnum(kReasonEvictingForNewEntry);
1112         ValidateNextEnum(kReasonRemovingEid);
1113     };
1114 
1115     LogInfo("Cache entry %s: %s, 0x%04x%s%s - %s", kChangeStrings[aChange], aEntry.GetTarget().ToString().AsCString(),
1116             aEntry.GetRloc16(), (aList == nullptr) ? "" : ", list:", ListToString(aList), kReasonStrings[aReason]);
1117 }
1118 
ListToString(const CacheEntryList * aList) const1119 const char *AddressResolver::ListToString(const CacheEntryList *aList) const
1120 {
1121     const char *str = "";
1122 
1123     VerifyOrExit(aList != &mCachedList, str = "cached");
1124     VerifyOrExit(aList != &mSnoopedList, str = "snooped");
1125     VerifyOrExit(aList != &mQueryList, str = "query");
1126     VerifyOrExit(aList != &mQueryRetryList, str = "query-retry");
1127 
1128 exit:
1129     return str;
1130 }
1131 
1132 #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1133 
LogCacheEntryChange(EntryChange,Reason,const CacheEntry &,CacheEntryList *)1134 void AddressResolver::LogCacheEntryChange(EntryChange, Reason, const CacheEntry &, CacheEntryList *) {}
1135 
1136 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
1137 
1138 // LCOV_EXCL_STOP
1139 
1140 //---------------------------------------------------------------------------------------------------------------------
1141 // AddressResolver::CacheEntry
1142 
Init(Instance & aInstance)1143 void AddressResolver::CacheEntry::Init(Instance &aInstance)
1144 {
1145     InstanceLocatorInit::Init(aInstance);
1146     mNextIndex        = kNoNextIndex;
1147     mFreshnessTimeout = 0;
1148 }
1149 
GetNext(void)1150 AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void)
1151 {
1152     return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().GetCacheEntryPool().GetEntryAt(mNextIndex);
1153 }
1154 
GetNext(void) const1155 const AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) const
1156 {
1157     return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().GetCacheEntryPool().GetEntryAt(mNextIndex);
1158 }
1159 
SetNext(CacheEntry * aEntry)1160 void AddressResolver::CacheEntry::SetNext(CacheEntry *aEntry)
1161 {
1162     VerifyOrExit(aEntry != nullptr, mNextIndex = kNoNextIndex);
1163     mNextIndex = Get<AddressResolver>().GetCacheEntryPool().GetIndexOf(*aEntry);
1164 
1165 exit:
1166     return;
1167 }
1168 
1169 #endif // OPENTHREAD_FTD
1170 
1171 } // namespace ot
1172