• 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 "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     , mAddressError(UriPath::kAddressError, &AddressResolver::HandleAddressError, this)
58 #if OPENTHREAD_FTD
59     , mAddressQuery(UriPath::kAddressQuery, &AddressResolver::HandleAddressQuery, this)
60     , mAddressNotification(UriPath::kAddressNotify, &AddressResolver::HandleAddressNotification, this)
61     , mCacheEntryPool(aInstance)
62     , mIcmpHandler(&AddressResolver::HandleIcmpReceive, this)
63 #endif
64 {
65     Get<Tmf::Agent>().AddResource(mAddressError);
66 #if OPENTHREAD_FTD
67     Get<Tmf::Agent>().AddResource(mAddressQuery);
68     Get<Tmf::Agent>().AddResource(mAddressNotification);
69 
70     IgnoreError(Get<Ip6::Icmp>().RegisterHandler(mIcmpHandler));
71 #endif
72 }
73 
74 #if OPENTHREAD_FTD
75 
Clear(void)76 void AddressResolver::Clear(void)
77 {
78     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList};
79 
80     for (CacheEntryList *list : lists)
81     {
82         CacheEntry *entry;
83 
84         while ((entry = list->Pop()) != nullptr)
85         {
86             if (list == &mQueryList)
87             {
88                 Get<MeshForwarder>().HandleResolved(entry->GetTarget(), kErrorDrop);
89             }
90 
91             mCacheEntryPool.Free(*entry);
92         }
93     }
94 }
95 
GetNextCacheEntry(EntryInfo & aInfo,Iterator & aIterator) const96 Error AddressResolver::GetNextCacheEntry(EntryInfo &aInfo, Iterator &aIterator) const
97 {
98     Error                 error = kErrorNone;
99     const CacheEntryList *list;
100     const CacheEntry *    entry;
101 
102     list  = reinterpret_cast<const CacheEntryList *>(aIterator.mData[kIteratorListIndex]);
103     entry = reinterpret_cast<const CacheEntry *>(aIterator.mData[kIteratorEntryIndex]);
104 
105     while (entry == nullptr)
106     {
107         if (list == nullptr)
108         {
109             list = &mCachedList;
110         }
111         else if (list == &mCachedList)
112         {
113             list = &mSnoopedList;
114         }
115         else if (list == &mSnoopedList)
116         {
117             list = &mQueryList;
118         }
119         else if (list == &mQueryList)
120         {
121             list = &mQueryRetryList;
122         }
123         else
124         {
125             ExitNow(error = kErrorNotFound);
126         }
127 
128         entry = list->GetHead();
129     }
130 
131     // Update the iterator then populate the `aInfo`.
132 
133     aIterator.mData[kIteratorEntryIndex] = entry->GetNext();
134     aIterator.mData[kIteratorListIndex]  = list;
135 
136     memset(&aInfo, 0, sizeof(aInfo));
137     aInfo.mTarget = entry->GetTarget();
138     aInfo.mRloc16 = entry->GetRloc16();
139 
140     if (list == &mCachedList)
141     {
142         aInfo.mState          = OT_CACHE_ENTRY_STATE_CACHED;
143         aInfo.mCanEvict       = true;
144         aInfo.mValidLastTrans = entry->IsLastTransactionTimeValid();
145 
146         VerifyOrExit(entry->IsLastTransactionTimeValid());
147 
148         aInfo.mLastTransTime = entry->GetLastTransactionTime();
149         AsCoreType(&aInfo.mMeshLocalEid).SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix());
150         AsCoreType(&aInfo.mMeshLocalEid).SetIid(entry->GetMeshLocalIid());
151 
152         ExitNow();
153     }
154 
155     if (list == &mSnoopedList)
156     {
157         aInfo.mState = OT_CACHE_ENTRY_STATE_SNOOPED;
158     }
159     else if (list == &mQueryList)
160     {
161         aInfo.mState = OT_CACHE_ENTRY_STATE_QUERY;
162     }
163     else
164     {
165         aInfo.mState = OT_CACHE_ENTRY_STATE_RETRY_QUERY;
166     }
167 
168     aInfo.mCanEvict   = entry->CanEvict();
169     aInfo.mTimeout    = entry->GetTimeout();
170     aInfo.mRetryDelay = entry->GetRetryDelay();
171 
172 exit:
173     return error;
174 }
175 
Remove(uint8_t aRouterId)176 void AddressResolver::Remove(uint8_t aRouterId)
177 {
178     Remove(Mle::Mle::Rloc16FromRouterId(aRouterId), /* aMatchRouterId */ true);
179 }
180 
Remove(uint16_t aRloc16)181 void AddressResolver::Remove(uint16_t aRloc16)
182 {
183     Remove(aRloc16, /* aMatchRouterId */ false);
184 }
185 
GetEntryAfter(CacheEntry * aPrev,CacheEntryList & aList)186 AddressResolver::CacheEntry *AddressResolver::GetEntryAfter(CacheEntry *aPrev, CacheEntryList &aList)
187 {
188     return (aPrev == nullptr) ? aList.GetHead() : aPrev->GetNext();
189 }
190 
Remove(Mac::ShortAddress aRloc16,bool aMatchRouterId)191 void AddressResolver::Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId)
192 {
193     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList};
194 
195     for (CacheEntryList *list : lists)
196     {
197         CacheEntry *prev = nullptr;
198         CacheEntry *entry;
199 
200         while ((entry = GetEntryAfter(prev, *list)) != nullptr)
201         {
202             if ((aMatchRouterId && Mle::Mle::RouterIdMatch(entry->GetRloc16(), aRloc16)) ||
203                 (!aMatchRouterId && (entry->GetRloc16() == aRloc16)))
204             {
205                 RemoveCacheEntry(*entry, *list, prev, aMatchRouterId ? kReasonRemovingRouterId : kReasonRemovingRloc16);
206                 mCacheEntryPool.Free(*entry);
207 
208                 // If the entry is removed from list, we keep the same
209                 // `prev` pointer.
210             }
211             else
212             {
213                 prev = entry;
214             }
215         }
216     }
217 }
218 
FindCacheEntry(const Ip6::Address & aEid,CacheEntryList * & aList,CacheEntry * & aPrevEntry)219 AddressResolver::CacheEntry *AddressResolver::FindCacheEntry(const Ip6::Address &aEid,
220                                                              CacheEntryList *&   aList,
221                                                              CacheEntry *&       aPrevEntry)
222 {
223     CacheEntry *    entry   = nullptr;
224     CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList};
225 
226     for (CacheEntryList *list : lists)
227     {
228         aList = list;
229         entry = aList->FindMatching(aEid, aPrevEntry);
230         VerifyOrExit(entry == nullptr);
231     }
232 
233 exit:
234     return entry;
235 }
236 
Remove(const Ip6::Address & aEid)237 void AddressResolver::Remove(const Ip6::Address &aEid)
238 {
239     Remove(aEid, kReasonRemovingEid);
240 }
241 
Remove(const Ip6::Address & aEid,Reason aReason)242 void AddressResolver::Remove(const Ip6::Address &aEid, Reason aReason)
243 {
244     CacheEntry *    entry;
245     CacheEntry *    prev;
246     CacheEntryList *list;
247 
248     entry = FindCacheEntry(aEid, list, prev);
249     VerifyOrExit(entry != nullptr);
250 
251     RemoveCacheEntry(*entry, *list, prev, aReason);
252     mCacheEntryPool.Free(*entry);
253 
254 exit:
255     return;
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     VerifyOrExit(UpdateCacheEntry(aEid, aRloc16) != kErrorNone);
390 
391     // Skip if the `aRloc16` (i.e., the source of the snooped message)
392     // is this device or an MTD (minimal) child of the device itself.
393 
394     macAddress = Get<Mac::Mac>().GetShortAddress();
395     VerifyOrExit((aRloc16 != macAddress) && !Get<Mle::MleRouter>().IsMinimalChild(aRloc16));
396 
397     // Ensure that the destination of the snooped message is this device
398     // or a minimal child of this device.
399 
400     VerifyOrExit((aDest == macAddress) || Get<Mle::MleRouter>().IsMinimalChild(aDest));
401 
402     entry = NewCacheEntry(/* aSnoopedEntry */ true);
403     VerifyOrExit(entry != nullptr);
404 
405     for (CacheEntry &snooped : mSnoopedList)
406     {
407         if (!snooped.CanEvict())
408         {
409             numNonEvictable++;
410         }
411     }
412 
413     entry->SetTarget(aEid);
414     entry->SetRloc16(aRloc16);
415 
416     if (numNonEvictable < kMaxNonEvictableSnoopedEntries)
417     {
418         entry->SetCanEvict(false);
419         entry->SetTimeout(kSnoopBlockEvictionTimeout);
420 
421         Get<TimeTicker>().RegisterReceiver(TimeTicker::kAddressResolver);
422     }
423     else
424     {
425         entry->SetCanEvict(true);
426         entry->SetTimeout(0);
427     }
428 
429     mSnoopedList.Push(*entry);
430 
431     LogCacheEntryChange(kEntryAdded, kReasonSnoop, *entry);
432 
433 exit:
434     return;
435 }
436 
RestartAddressQueries(void)437 void AddressResolver::RestartAddressQueries(void)
438 {
439     CacheEntry *tail;
440 
441     // We move all entries from `mQueryRetryList` at the tail of
442     // `mQueryList` and then (re)send Address Query for all entries in
443     // the updated `mQueryList`.
444 
445     tail = mQueryList.GetTail();
446 
447     if (tail == nullptr)
448     {
449         mQueryList.SetHead(mQueryRetryList.GetHead());
450     }
451     else
452     {
453         tail->SetNext(mQueryRetryList.GetHead());
454     }
455 
456     mQueryRetryList.Clear();
457 
458     for (CacheEntry &entry : mQueryList)
459     {
460         IgnoreError(SendAddressQuery(entry.GetTarget()));
461 
462         entry.SetTimeout(kAddressQueryTimeout);
463         entry.SetRetryDelay(kAddressQueryInitialRetryDelay);
464         entry.SetCanEvict(false);
465     }
466 }
467 
LookUp(const Ip6::Address & aEid)468 Mac::ShortAddress AddressResolver::LookUp(const Ip6::Address &aEid)
469 {
470     Mac::ShortAddress rloc16 = Mac::kShortAddrInvalid;
471 
472     IgnoreError(Resolve(aEid, rloc16, /* aAllowAddressQuery */ false));
473     return rloc16;
474 }
475 
Resolve(const Ip6::Address & aEid,Mac::ShortAddress & aRloc16,bool aAllowAddressQuery)476 Error AddressResolver::Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16, bool aAllowAddressQuery)
477 {
478     Error           error = kErrorNone;
479     CacheEntry *    entry;
480     CacheEntry *    prev = nullptr;
481     CacheEntryList *list;
482 
483     entry = FindCacheEntry(aEid, list, prev);
484 
485     if (entry == nullptr)
486     {
487         // If the entry is not present in any of the lists, try to
488         // allocate a new entry and perform address query. We do not
489         // allow first-time address query entries to be evicted till
490         // timeout.
491 
492         VerifyOrExit(aAllowAddressQuery, error = kErrorNotFound);
493 
494         entry = NewCacheEntry(/* aSnoopedEntry */ false);
495         VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
496 
497         entry->SetTarget(aEid);
498         entry->SetRloc16(Mac::kShortAddrInvalid);
499         entry->SetRetryDelay(kAddressQueryInitialRetryDelay);
500         entry->SetCanEvict(false);
501         list = nullptr;
502     }
503 
504     if ((list == &mCachedList) || (list == &mSnoopedList))
505     {
506         // Remove the entry from its current list and push it at the
507         // head of cached list.
508 
509         list->PopAfter(prev);
510 
511         if (list == &mSnoopedList)
512         {
513             entry->MarkLastTransactionTimeAsInvalid();
514         }
515 
516         mCachedList.Push(*entry);
517         aRloc16 = entry->GetRloc16();
518         ExitNow();
519     }
520 
521     // Note that if `aAllowAddressQuery` is `false` then the `entry`
522     // is definitely already in a list, i.e., we cannot not get here
523     // with `aAllowAddressQuery` being `false` and `entry` being a
524     // newly allocated one, due to the `VerifyOrExit` check that
525     // `aAllowAddressQuery` is `true` before allocating a new cache
526     // entry.
527     VerifyOrExit(aAllowAddressQuery, error = kErrorNotFound);
528 
529     if (list == &mQueryList)
530     {
531         ExitNow(error = kErrorAddressQuery);
532     }
533 
534     if (list == &mQueryRetryList)
535     {
536         // Allow an entry in query-retry mode to resend an Address
537         // Query again only if the timeout (retry delay interval) is
538         // expired.
539 
540         VerifyOrExit(entry->IsTimeoutZero(), error = kErrorDrop);
541         mQueryRetryList.PopAfter(prev);
542     }
543 
544     entry->SetTimeout(kAddressQueryTimeout);
545 
546     error = SendAddressQuery(aEid);
547     VerifyOrExit(error == kErrorNone, mCacheEntryPool.Free(*entry));
548 
549     if (list == nullptr)
550     {
551         LogCacheEntryChange(kEntryAdded, kReasonQueryRequest, *entry);
552     }
553 
554     mQueryList.Push(*entry);
555     error = kErrorAddressQuery;
556 
557 exit:
558     return error;
559 }
560 
SendAddressQuery(const Ip6::Address & aEid)561 Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid)
562 {
563     Error            error;
564     Coap::Message *  message;
565     Tmf::MessageInfo messageInfo(GetInstance());
566 
567     message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(UriPath::kAddressQuery);
568     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
569 
570     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aEid));
571 
572     messageInfo.SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast();
573 
574     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
575 
576     LogInfo("Sending address query for %s", aEid.ToString().AsCString());
577 
578 exit:
579 
580     Get<TimeTicker>().RegisterReceiver(TimeTicker::kAddressResolver);
581     FreeMessageOnError(message, error);
582 
583 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
584     if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(aEid))
585     {
586         uint16_t selfRloc16 = Get<Mle::MleRouter>().GetRloc16();
587 
588         LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x(self)", aEid.ToString().AsCString(),
589                 selfRloc16);
590         IgnoreError(Get<BackboneRouter::Manager>().SendBackboneQuery(aEid, selfRloc16));
591     }
592 #endif
593 
594     return error;
595 }
596 
HandleAddressNotification(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)597 void AddressResolver::HandleAddressNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
598 {
599     static_cast<AddressResolver *>(aContext)->HandleAddressNotification(AsCoapMessage(aMessage),
600                                                                         AsCoreType(aMessageInfo));
601 }
602 
HandleAddressNotification(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)603 void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
604 {
605     Ip6::Address             target;
606     Ip6::InterfaceIdentifier meshLocalIid;
607     uint16_t                 rloc16;
608     uint32_t                 lastTransactionTime;
609     CacheEntryList *         list;
610     CacheEntry *             entry;
611     CacheEntry *             prev;
612 
613     VerifyOrExit(aMessage.IsConfirmablePostRequest());
614 
615     SuccessOrExit(Tlv::Find<ThreadTargetTlv>(aMessage, target));
616     SuccessOrExit(Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
617     SuccessOrExit(Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16));
618 
619     switch (Tlv::Find<ThreadLastTransactionTimeTlv>(aMessage, lastTransactionTime))
620     {
621     case kErrorNone:
622         break;
623     case kErrorNotFound:
624         lastTransactionTime = 0;
625         break;
626     default:
627         ExitNow();
628     }
629 
630     LogInfo("Received address notification from 0x%04x for %s to 0x%04x",
631             aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString(), rloc16);
632 
633     entry = FindCacheEntry(target, list, prev);
634     VerifyOrExit(entry != nullptr);
635 
636     if (list == &mCachedList)
637     {
638         if (entry->IsLastTransactionTimeValid())
639         {
640             // Receiving multiple Address Notification for an EID from
641             // different mesh-local IIDs indicates address is in use
642             // by more than one device. Try to resolve the duplicate
643             // address by sending an Address Error message.
644 
645             VerifyOrExit(entry->GetMeshLocalIid() == meshLocalIid, SendAddressError(target, meshLocalIid, nullptr));
646 
647             VerifyOrExit(lastTransactionTime < entry->GetLastTransactionTime());
648         }
649     }
650 
651     entry->SetRloc16(rloc16);
652     entry->SetMeshLocalIid(meshLocalIid);
653     entry->SetLastTransactionTime(lastTransactionTime);
654 
655     list->PopAfter(prev);
656     mCachedList.Push(*entry);
657 
658     LogCacheEntryChange(kEntryUpdated, kReasonReceivedNotification, *entry);
659 
660     if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
661     {
662         LogInfo("Sending address notification acknowledgment");
663     }
664 
665     Get<MeshForwarder>().HandleResolved(target, kErrorNone);
666 
667 exit:
668     return;
669 }
670 
SendAddressError(const Ip6::Address & aTarget,const Ip6::InterfaceIdentifier & aMeshLocalIid,const Ip6::Address * aDestination)671 void AddressResolver::SendAddressError(const Ip6::Address &            aTarget,
672                                        const Ip6::InterfaceIdentifier &aMeshLocalIid,
673                                        const Ip6::Address *            aDestination)
674 {
675     Error            error;
676     Coap::Message *  message;
677     Tmf::MessageInfo messageInfo(GetInstance());
678 
679     VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
680 
681     message->Init(aDestination == nullptr ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost);
682     SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kAddressError));
683     SuccessOrExit(error = message->SetPayloadMarker());
684 
685     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aTarget));
686     SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
687 
688     if (aDestination == nullptr)
689     {
690         messageInfo.SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast();
691     }
692     else
693     {
694         messageInfo.SetSockAddrToRlocPeerAddrTo(*aDestination);
695     }
696 
697     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
698 
699     LogInfo("Sending address error for target %s", aTarget.ToString().AsCString());
700 
701 exit:
702 
703     if (error != kErrorNone)
704     {
705         FreeMessage(message);
706         LogInfo("Failed to send address error: %s", ErrorToString(error));
707     }
708 }
709 
710 #endif // OPENTHREAD_FTD
711 
HandleAddressError(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)712 void AddressResolver::HandleAddressError(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
713 {
714     static_cast<AddressResolver *>(aContext)->HandleAddressError(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
715 }
716 
HandleAddressError(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)717 void AddressResolver::HandleAddressError(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
718 {
719     Error                    error = kErrorNone;
720     Ip6::Address             target;
721     Ip6::InterfaceIdentifier meshLocalIid;
722 #if OPENTHREAD_FTD
723     Mac::ExtAddress extAddr;
724     Ip6::Address    destination;
725 #endif
726 
727     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
728 
729     LogInfo("Received address error notification");
730 
731     if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
732     {
733         if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
734         {
735             LogInfo("Sent address error notification acknowledgment");
736         }
737     }
738 
739     SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
740     SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
741 
742     for (const Ip6::Netif::UnicastAddress &address : Get<ThreadNetif>().GetUnicastAddresses())
743     {
744         if (address.GetAddress() == target && Get<Mle::MleRouter>().GetMeshLocal64().GetIid() != meshLocalIid)
745         {
746             // Target EID matches address and Mesh Local EID differs
747 #if OPENTHREAD_CONFIG_DUA_ENABLE
748             if (Get<BackboneRouter::Leader>().IsDomainUnicast(address.GetAddress()))
749             {
750                 Get<DuaManager>().NotifyDuplicateDomainUnicastAddress();
751             }
752             else
753 #endif
754             {
755                 Get<ThreadNetif>().RemoveUnicastAddress(address);
756             }
757 
758             ExitNow();
759         }
760     }
761 
762 #if OPENTHREAD_FTD
763     meshLocalIid.ConvertToExtAddress(extAddr);
764 
765     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
766     {
767         if (child.IsFullThreadDevice())
768         {
769             continue;
770         }
771 
772         if (child.GetExtAddress() != extAddr)
773         {
774             // Mesh Local EID differs, so check whether Target EID
775             // matches a child address and if so remove it.
776 
777             if (child.RemoveIp6Address(target) == kErrorNone)
778             {
779                 SuccessOrExit(error = Get<Mle::Mle>().GetLocatorAddress(destination, child.GetRloc16()));
780 
781                 SendAddressError(target, meshLocalIid, &destination);
782                 ExitNow();
783             }
784         }
785     }
786 #endif // OPENTHREAD_FTD
787 
788 exit:
789 
790     if (error != kErrorNone)
791     {
792         LogWarn("Error while processing address error notification: %s", ErrorToString(error));
793     }
794 }
795 
796 #if OPENTHREAD_FTD
797 
HandleAddressQuery(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)798 void AddressResolver::HandleAddressQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
799 {
800     static_cast<AddressResolver *>(aContext)->HandleAddressQuery(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
801 }
802 
HandleAddressQuery(Coap::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)803 void AddressResolver::HandleAddressQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
804 {
805     Ip6::Address target;
806     uint32_t     lastTransactionTime;
807 
808     VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
809 
810     SuccessOrExit(Tlv::Find<ThreadTargetTlv>(aMessage, target));
811 
812     LogInfo("Received address query from 0x%04x for target %s", aMessageInfo.GetPeerAddr().GetIid().GetLocator(),
813             target.ToString().AsCString());
814 
815     if (Get<ThreadNetif>().HasUnicastAddress(target))
816     {
817         SendAddressQueryResponse(target, Get<Mle::MleRouter>().GetMeshLocal64().GetIid(), nullptr,
818                                  aMessageInfo.GetPeerAddr());
819         ExitNow();
820     }
821 
822     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
823     {
824         if (child.IsFullThreadDevice() || child.GetLinkFailures() >= Mle::kFailedChildTransmissions)
825         {
826             continue;
827         }
828 
829         if (child.HasIp6Address(target))
830         {
831             lastTransactionTime = Time::MsecToSec(TimerMilli::GetNow() - child.GetLastHeard());
832             SendAddressQueryResponse(target, child.GetMeshLocalIid(), &lastTransactionTime, aMessageInfo.GetPeerAddr());
833             ExitNow();
834         }
835     }
836 
837 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
838     if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(target))
839     {
840         uint16_t srcRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
841 
842         LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x", target.ToString().AsCString(), srcRloc16);
843         IgnoreError(Get<BackboneRouter::Manager>().SendBackboneQuery(target, srcRloc16));
844     }
845 #endif
846 
847 exit:
848     return;
849 }
850 
SendAddressQueryResponse(const Ip6::Address & aTarget,const Ip6::InterfaceIdentifier & aMeshLocalIid,const uint32_t * aLastTransactionTime,const Ip6::Address & aDestination)851 void AddressResolver::SendAddressQueryResponse(const Ip6::Address &            aTarget,
852                                                const Ip6::InterfaceIdentifier &aMeshLocalIid,
853                                                const uint32_t *                aLastTransactionTime,
854                                                const Ip6::Address &            aDestination)
855 {
856     Error            error;
857     Coap::Message *  message;
858     Tmf::MessageInfo messageInfo(GetInstance());
859 
860     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kAddressNotify);
861     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
862 
863     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aTarget));
864     SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
865     SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, Get<Mle::MleRouter>().GetRloc16()));
866 
867     if (aLastTransactionTime != nullptr)
868     {
869         SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, *aLastTransactionTime));
870     }
871 
872     messageInfo.SetSockAddrToRlocPeerAddrTo(aDestination);
873 
874     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
875 
876     LogInfo("Sending address notification for target %s", aTarget.ToString().AsCString());
877 
878 exit:
879     FreeMessageOnError(message, error);
880 }
881 
HandleTimeTick(void)882 void AddressResolver::HandleTimeTick(void)
883 {
884     bool continueRxingTicks = false;
885 
886     for (CacheEntry &entry : mSnoopedList)
887     {
888         if (entry.IsTimeoutZero())
889         {
890             continue;
891         }
892 
893         continueRxingTicks = true;
894         entry.DecrementTimeout();
895 
896         if (entry.IsTimeoutZero())
897         {
898             entry.SetCanEvict(true);
899         }
900     }
901 
902     for (CacheEntry &entry : mQueryRetryList)
903     {
904         if (entry.IsTimeoutZero())
905         {
906             continue;
907         }
908 
909         continueRxingTicks = true;
910         entry.DecrementTimeout();
911     }
912 
913     {
914         CacheEntry *prev = nullptr;
915         CacheEntry *entry;
916 
917         while ((entry = GetEntryAfter(prev, mQueryList)) != nullptr)
918         {
919             OT_ASSERT(!entry->IsTimeoutZero());
920 
921             continueRxingTicks = true;
922             entry->DecrementTimeout();
923 
924             if (entry->IsTimeoutZero())
925             {
926                 uint16_t retryDelay = entry->GetRetryDelay();
927 
928                 entry->SetTimeout(retryDelay);
929 
930                 retryDelay <<= 1;
931 
932                 if (retryDelay > kAddressQueryMaxRetryDelay)
933                 {
934                     retryDelay = kAddressQueryMaxRetryDelay;
935                 }
936 
937                 entry->SetRetryDelay(retryDelay);
938                 entry->SetCanEvict(true);
939 
940                 // Move the entry from `mQueryList` to `mQueryRetryList`
941                 mQueryList.PopAfter(prev);
942                 mQueryRetryList.Push(*entry);
943 
944                 LogInfo("Timed out waiting for address notification for %s, retry: %d",
945                         entry->GetTarget().ToString().AsCString(), entry->GetTimeout());
946 
947                 Get<MeshForwarder>().HandleResolved(entry->GetTarget(), kErrorDrop);
948 
949                 // When the entry is removed from `mQueryList`
950                 // we keep the `prev` pointer same as before.
951             }
952             else
953             {
954                 prev = entry;
955             }
956         }
957     }
958 
959     if (!continueRxingTicks)
960     {
961         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kAddressResolver);
962     }
963 }
964 
HandleIcmpReceive(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,const otIcmp6Header * aIcmpHeader)965 void AddressResolver::HandleIcmpReceive(void *               aContext,
966                                         otMessage *          aMessage,
967                                         const otMessageInfo *aMessageInfo,
968                                         const otIcmp6Header *aIcmpHeader)
969 {
970     OT_UNUSED_VARIABLE(aMessageInfo);
971 
972     static_cast<AddressResolver *>(aContext)->HandleIcmpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo),
973                                                                 AsCoreType(aIcmpHeader));
974 }
975 
HandleIcmpReceive(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,const Ip6::Icmp::Header & aIcmpHeader)976 void AddressResolver::HandleIcmpReceive(Message &                aMessage,
977                                         const Ip6::MessageInfo & aMessageInfo,
978                                         const Ip6::Icmp::Header &aIcmpHeader)
979 {
980     OT_UNUSED_VARIABLE(aMessageInfo);
981 
982     Ip6::Header ip6Header;
983 
984     VerifyOrExit(aIcmpHeader.GetType() == Ip6::Icmp::Header::kTypeDstUnreach);
985     VerifyOrExit(aIcmpHeader.GetCode() == Ip6::Icmp::Header::kCodeDstUnreachNoRoute);
986     SuccessOrExit(aMessage.Read(aMessage.GetOffset(), ip6Header));
987 
988     Remove(ip6Header.GetDestination(), kReasonReceivedIcmpDstUnreachNoRoute);
989 
990 exit:
991     return;
992 }
993 
994 // LCOV_EXCL_START
995 
996 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
997 
LogCacheEntryChange(EntryChange aChange,Reason aReason,const CacheEntry & aEntry,CacheEntryList * aList)998 void AddressResolver::LogCacheEntryChange(EntryChange       aChange,
999                                           Reason            aReason,
1000                                           const CacheEntry &aEntry,
1001                                           CacheEntryList *  aList)
1002 {
1003     static const char *const kChangeStrings[] = {
1004         "added",   // (0) kEntryAdded
1005         "updated", // (1) kEntryUpdated
1006         "removed", // (2) kEntryRemoved
1007     };
1008 
1009     static const char *const kReasonStrings[] = {
1010         "query request",          // (0) kReasonQueryRequest
1011         "snoop",                  // (1) kReasonSnoop
1012         "rx notification",        // (2) kReasonReceivedNotification
1013         "removing router id",     // (3) kReasonRemovingRouterId
1014         "removing rloc16",        // (4) kReasonRemovingRloc16
1015         "rx icmp no route",       // (5) kReasonReceivedIcmpDstUnreachNoRoute
1016         "evicting for new entry", // (6) kReasonEvictingForNewEntry
1017         "removing eid",           // (7) kReasonRemovingEid
1018     };
1019 
1020     static_assert(0 == kEntryAdded, "kEntryAdded value is incorrect");
1021     static_assert(1 == kEntryUpdated, "kEntryUpdated value is incorrect");
1022     static_assert(2 == kEntryRemoved, "kEntryRemoved value is incorrect");
1023 
1024     static_assert(0 == kReasonQueryRequest, "kReasonQueryRequest value is incorrect");
1025     static_assert(1 == kReasonSnoop, "kReasonSnoop value is incorrect");
1026     static_assert(2 == kReasonReceivedNotification, "kReasonReceivedNotification value is incorrect");
1027     static_assert(3 == kReasonRemovingRouterId, "kReasonRemovingRouterId value is incorrect");
1028     static_assert(4 == kReasonRemovingRloc16, "kReasonRemovingRloc16 value is incorrect");
1029     static_assert(5 == kReasonReceivedIcmpDstUnreachNoRoute, "kReasonReceivedIcmpDstUnreachNoRoute value is incorrect");
1030     static_assert(6 == kReasonEvictingForNewEntry, "kReasonEvictingForNewEntry value is incorrect");
1031     static_assert(7 == kReasonRemovingEid, "kReasonRemovingEid value is incorrect");
1032 
1033     LogInfo("Cache entry %s: %s, 0x%04x%s%s - %s", kChangeStrings[aChange], aEntry.GetTarget().ToString().AsCString(),
1034             aEntry.GetRloc16(), (aList == nullptr) ? "" : ", list:", ListToString(aList), kReasonStrings[aReason]);
1035 }
1036 
ListToString(const CacheEntryList * aList) const1037 const char *AddressResolver::ListToString(const CacheEntryList *aList) const
1038 {
1039     const char *str = "";
1040 
1041     VerifyOrExit(aList != &mCachedList, str = "cached");
1042     VerifyOrExit(aList != &mSnoopedList, str = "snooped");
1043     VerifyOrExit(aList != &mQueryList, str = "query");
1044     VerifyOrExit(aList != &mQueryRetryList, str = "query-retry");
1045 
1046 exit:
1047     return str;
1048 }
1049 
1050 #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
1051 
LogCacheEntryChange(EntryChange,Reason,const CacheEntry &,CacheEntryList *)1052 void AddressResolver::LogCacheEntryChange(EntryChange, Reason, const CacheEntry &, CacheEntryList *)
1053 {
1054 }
1055 
1056 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
1057 
1058 // LCOV_EXCL_STOP
1059 
1060 //---------------------------------------------------------------------------------------------------------------------
1061 // AddressResolver::CacheEntry
1062 
Init(Instance & aInstance)1063 void AddressResolver::CacheEntry::Init(Instance &aInstance)
1064 {
1065     InstanceLocatorInit::Init(aInstance);
1066     mNextIndex = kNoNextIndex;
1067 }
1068 
GetNext(void)1069 AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void)
1070 {
1071     return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().GetCacheEntryPool().GetEntryAt(mNextIndex);
1072 }
1073 
GetNext(void) const1074 const AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) const
1075 {
1076     return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().GetCacheEntryPool().GetEntryAt(mNextIndex);
1077 }
1078 
SetNext(CacheEntry * aEntry)1079 void AddressResolver::CacheEntry::SetNext(CacheEntry *aEntry)
1080 {
1081     VerifyOrExit(aEntry != nullptr, mNextIndex = kNoNextIndex);
1082     mNextIndex = Get<AddressResolver>().GetCacheEntryPool().GetIndexOf(*aEntry);
1083 
1084 exit:
1085     return;
1086 }
1087 
1088 #endif // OPENTHREAD_FTD
1089 
1090 } // namespace ot
1091