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