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