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