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