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