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