mirror of
https://github.com/espressif/openthread.git
synced 2026-10-11 02:07:40 +00:00
[address-resolver] use linked-list and enable snooped entry timeout (#4599)
This commit contains multiple enhancements in `AddressResolver`: Cache entries are stored in different lists: Cached list (entries in use), query list (entries actively querying and waiting for address notification response), query-retry list (entries in delay wait mode due to prior query failing to get a response), and snooped list (entries discovered through received message inspection aka snoop optimization). Singly linked list is used which helps keep the entries sorted based on the order of use (whenever a cache entry is used to resolve an address, it is moved to the head of the list). This replaces and simplifies the aging model. This commit updates the cache entry class definition to use `union` for member variables (like timeout or transaction time) that are tied to entry being in different lists. This help reduce the memory requirement for cache table. This commit also adds a new mechanism to manage snooped entries. When a new snooped entry is added, we do not allow it to be evicted for a short timeout. This allows some delay for a response message to use the entry (if entry is used it will be moved to the cached list). If a snooped entry is not used after the timeout, we allow it to be evicted. To ensure snooped entries do not overwrite other cached entries, we limit the number of snooped entries that are in timeout mode and cannot be evicted by a (build-time configurable) threshold.
This commit is contained in:
committed by
Jonathan Hui
parent
8a5f538e6f
commit
17617a2ee4
@@ -45,6 +45,31 @@
|
||||
#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES
|
||||
*
|
||||
* The maximum number of EID-to-RLOC cache entries that can be used for "snoop optimization" where an entry is created
|
||||
* by inspecting a received message.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES
|
||||
#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES 2
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT
|
||||
*
|
||||
* The timeout value (in seconds) blocking eviction of an address cache entry created through snoop optimization (i.e.,
|
||||
* inspection of a received message). After the timeout expires the entry can be reclaimed again. This timeout allows
|
||||
* a longer response delay for a received message giving more chance that a snooped entry will be used (avoiding
|
||||
* sending Address Query when a response message is sent to same destination from which the message was received
|
||||
* earlier).
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT
|
||||
#define OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT 3
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT
|
||||
*
|
||||
|
||||
@@ -57,10 +57,19 @@ AddressResolver::AddressResolver(Instance &aInstance)
|
||||
, mAddressError(OT_URI_PATH_ADDRESS_ERROR, &AddressResolver::HandleAddressError, this)
|
||||
, mAddressQuery(OT_URI_PATH_ADDRESS_QUERY, &AddressResolver::HandleAddressQuery, this)
|
||||
, mAddressNotification(OT_URI_PATH_ADDRESS_NOTIFY, &AddressResolver::HandleAddressNotification, this)
|
||||
, mCachedList()
|
||||
, mSnoopedList()
|
||||
, mQueryList()
|
||||
, mQueryRetryList()
|
||||
, mUnusedList()
|
||||
, mIcmpHandler(&AddressResolver::HandleIcmpReceive, this)
|
||||
, mTimer(aInstance, &AddressResolver::HandleTimer, this)
|
||||
{
|
||||
Init();
|
||||
for (CacheEntry *entry = &mCacheEntries[0]; entry < OT_ARRAY_END(mCacheEntries); entry++)
|
||||
{
|
||||
entry->Init(GetInstance());
|
||||
mUnusedList.Push(*entry);
|
||||
}
|
||||
|
||||
Get<Coap::Coap>().AddResource(mAddressError);
|
||||
Get<Coap::Coap>().AddResource(mAddressQuery);
|
||||
@@ -69,41 +78,51 @@ AddressResolver::AddressResolver(Instance &aInstance)
|
||||
Get<Ip6::Icmp>().RegisterHandler(mIcmpHandler);
|
||||
}
|
||||
|
||||
void AddressResolver::Init(void)
|
||||
{
|
||||
memset(&mCache, 0, sizeof(mCache));
|
||||
|
||||
for (uint8_t i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
mCache[i].mAge = i;
|
||||
}
|
||||
}
|
||||
|
||||
void AddressResolver::Clear(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < kCacheEntries; i++)
|
||||
CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList};
|
||||
|
||||
for (uint8_t index = 0; index < OT_ARRAY_LENGTH(lists); index++)
|
||||
{
|
||||
if (mCache[i].mState != Cache::kStateQuery)
|
||||
CacheEntryList *list = lists[index];
|
||||
CacheEntry * entry;
|
||||
|
||||
while ((entry = list->Pop()) != NULL)
|
||||
{
|
||||
continue;
|
||||
if (list == &mQueryList)
|
||||
{
|
||||
Get<MeshForwarder>().HandleResolved(entry->GetTarget(), OT_ERROR_DROP);
|
||||
}
|
||||
|
||||
mUnusedList.Push(*entry);
|
||||
}
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(mCache[i].mTarget, OT_ERROR_DROP);
|
||||
}
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
otError AddressResolver::GetEntry(uint8_t aIndex, otEidCacheEntry &aEntry) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint8_t age = aIndex;
|
||||
const CacheEntryList *lists[] = {&mCachedList, &mSnoopedList};
|
||||
const CacheEntry * entry;
|
||||
|
||||
VerifyOrExit(aIndex < kCacheEntries, error = OT_ERROR_INVALID_ARGS);
|
||||
for (uint8_t index = 0; index < OT_ARRAY_LENGTH(lists); index++)
|
||||
{
|
||||
for (entry = lists[index]->GetHead(); (aIndex != 0) && (entry != NULL); aIndex--, entry = entry->GetNext())
|
||||
;
|
||||
|
||||
aEntry.mTarget = mCache[aIndex].mTarget;
|
||||
aEntry.mRloc16 = mCache[aIndex].mRloc16;
|
||||
aEntry.mAge = mCache[aIndex].mAge;
|
||||
aEntry.mValid = mCache[aIndex].mState == Cache::kStateCached;
|
||||
if (entry != NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(entry != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
aEntry.mTarget = entry->GetTarget();
|
||||
aEntry.mRloc16 = entry->GetRloc16();
|
||||
aEntry.mAge = age;
|
||||
aEntry.mValid = true;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -111,241 +130,259 @@ exit:
|
||||
|
||||
void AddressResolver::Remove(uint8_t aRouterId)
|
||||
{
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (Mle::Mle::RouterIdFromRloc16(mCache[i].mRloc16) == aRouterId)
|
||||
{
|
||||
InvalidateCacheEntry(mCache[i], kReasonRemovingRouterId);
|
||||
}
|
||||
}
|
||||
Remove(Mle::Mle::Rloc16FromRouterId(aRouterId), /* aMatchRouterId */ true);
|
||||
}
|
||||
|
||||
void AddressResolver::Remove(uint16_t aRloc16)
|
||||
{
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
Remove(aRloc16, /* aMatchRouterId */ false);
|
||||
}
|
||||
|
||||
void AddressResolver::Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId)
|
||||
{
|
||||
CacheEntryList *lists[] = {&mCachedList, &mSnoopedList};
|
||||
|
||||
for (uint8_t index = 0; index < OT_ARRAY_LENGTH(lists); index++)
|
||||
{
|
||||
if (mCache[i].mRloc16 == aRloc16)
|
||||
CacheEntryList *list = lists[index];
|
||||
CacheEntry * prev;
|
||||
CacheEntry * entry;
|
||||
CacheEntry * next;
|
||||
|
||||
for (prev = NULL, entry = list->GetHead(); entry != NULL; prev = entry, entry = next)
|
||||
{
|
||||
InvalidateCacheEntry(mCache[i], kReasonRemovingRloc16);
|
||||
// We keep track of the next entry in the list before
|
||||
// checking the entry's RLOC16 or Router Id since the
|
||||
// entry may be removed from the list if it matches.
|
||||
|
||||
next = entry->GetNext();
|
||||
|
||||
if ((aMatchRouterId && Mle::Mle::RouterIdMatch(entry->GetRloc16(), aRloc16)) ||
|
||||
(!aMatchRouterId && (entry->GetRloc16() == aRloc16)))
|
||||
{
|
||||
RemoveCacheEntry(*entry, *list, prev, aMatchRouterId ? kReasonRemovingRouterId : kReasonRemovingRloc16);
|
||||
mUnusedList.Push(*entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddressResolver::CacheEntry *AddressResolver::FindCacheEntryInList(CacheEntryList & aList,
|
||||
const Ip6::Address &aEid,
|
||||
CacheEntry *& aPrevEntry)
|
||||
{
|
||||
CacheEntry *entry;
|
||||
CacheEntry *prev;
|
||||
|
||||
for (prev = NULL, entry = aList.GetHead(); entry != NULL; prev = entry, entry = entry->GetNext())
|
||||
{
|
||||
if (entry->GetTarget() == aEid)
|
||||
{
|
||||
aPrevEntry = prev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
AddressResolver::CacheEntry *AddressResolver::FindCacheEntry(const Ip6::Address &aEid,
|
||||
CacheEntryList *& aList,
|
||||
CacheEntry *& aPrevEntry)
|
||||
{
|
||||
CacheEntry * entry = NULL;
|
||||
CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList};
|
||||
|
||||
for (uint8_t index = 0; index < OT_ARRAY_LENGTH(lists); index++)
|
||||
{
|
||||
aList = lists[index];
|
||||
entry = FindCacheEntryInList(*aList, aEid, aPrevEntry);
|
||||
VerifyOrExit(entry == NULL);
|
||||
}
|
||||
|
||||
exit:
|
||||
return entry;
|
||||
}
|
||||
|
||||
void AddressResolver::Remove(const Ip6::Address &aEid)
|
||||
{
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (mCache[i].mState == Cache::kStateInvalid || mCache[i].mTarget != aEid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
InvalidateCacheEntry(mCache[i], kReasonRemovingEid);
|
||||
break;
|
||||
}
|
||||
Remove(aEid, kReasonRemovingEid);
|
||||
}
|
||||
|
||||
AddressResolver::Cache *AddressResolver::NewCacheEntry(void)
|
||||
void AddressResolver::Remove(const Ip6::Address &aEid, Reason aReason)
|
||||
{
|
||||
Cache *rval = NULL;
|
||||
CacheEntry * entry;
|
||||
CacheEntry * prev;
|
||||
CacheEntryList *list;
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (mCache[i].mState == Cache::kStateQuery && mCache[i].mFailures == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
entry = FindCacheEntry(aEid, list, prev);
|
||||
VerifyOrExit(entry != NULL);
|
||||
|
||||
if (rval == NULL || rval->mAge < mCache[i].mAge)
|
||||
{
|
||||
rval = &mCache[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (rval != NULL)
|
||||
{
|
||||
InvalidateCacheEntry(*rval, kReasonEvictingForNewEntry);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void AddressResolver::MoveCacheEntryAt(Cache &aEntry, uint8_t aAge)
|
||||
{
|
||||
VerifyOrExit(aEntry.mAge != aAge);
|
||||
|
||||
if (aEntry.mAge > aAge)
|
||||
{
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (mCache[i].mAge >= aAge && mCache[i].mAge < aEntry.mAge)
|
||||
{
|
||||
mCache[i].mAge++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (mCache[i].mAge > aEntry.mAge && mCache[i].mAge <= aAge)
|
||||
{
|
||||
mCache[i].mAge--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aEntry.mAge = aAge;
|
||||
RemoveCacheEntry(*entry, *list, prev, aReason);
|
||||
mUnusedList.Push(*entry);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void AddressResolver::MoveCacheEntryBehindCached(Cache &aEntry)
|
||||
AddressResolver::CacheEntry *AddressResolver::NewCacheEntry(bool aSnoopedEntry)
|
||||
{
|
||||
bool found = false;
|
||||
uint8_t lastInCachedState = 0;
|
||||
CacheEntry * newEntry = NULL;
|
||||
CacheEntry * prevEntry = NULL;
|
||||
CacheEntryList *lists[] = {&mSnoopedList, &mQueryRetryList, &mQueryList, &mCachedList};
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
// The following order is used when trying to allocate a new cache
|
||||
// entry: First the unused list is checked, followed by the list
|
||||
// of snooped entries, then query-retry list (entries in delay
|
||||
// retry timeout wait due to a prior query failing to get a
|
||||
// response), then the query list (entries actively querying and
|
||||
// waiting for address notification response), and finally the
|
||||
// cached (in-use) list. Within each list the oldest entry is
|
||||
// reclaimed first (the list's tail). We also make sure the entry
|
||||
// can be evicted (e.g., first time query entries can not be
|
||||
// evicted till timeout).
|
||||
|
||||
newEntry = mUnusedList.Pop();
|
||||
VerifyOrExit(newEntry == NULL);
|
||||
|
||||
for (uint8_t index = 0; index < OT_ARRAY_LENGTH(lists); index++)
|
||||
{
|
||||
if (mCache[i].mState != Cache::kStateCached || mCache[i].mAge == aEntry.mAge)
|
||||
CacheEntryList *list = lists[index];
|
||||
CacheEntry * prev;
|
||||
CacheEntry * entry;
|
||||
uint16_t numNonEvictable = 0;
|
||||
|
||||
for (prev = NULL, entry = list->GetHead(); entry != NULL; prev = entry, entry = entry->GetNext())
|
||||
{
|
||||
continue;
|
||||
if ((list != &mCachedList) && !entry->CanEvict())
|
||||
{
|
||||
numNonEvictable++;
|
||||
continue;
|
||||
}
|
||||
|
||||
newEntry = entry;
|
||||
prevEntry = prev;
|
||||
}
|
||||
|
||||
if (mCache[i].mAge >= lastInCachedState)
|
||||
if (newEntry != NULL)
|
||||
{
|
||||
found = true;
|
||||
lastInCachedState = mCache[i].mAge;
|
||||
RemoveCacheEntry(*newEntry, *list, prevEntry, kReasonEvictingForNewEntry);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
if (aSnoopedEntry && (list == &mSnoopedList))
|
||||
{
|
||||
// Check if the new entry is being requested for "snoop
|
||||
// optimization" (i.e., inspection of a received message).
|
||||
// When a new snooped entry is added, we do not allow it
|
||||
// to be evicted for a short timeout. This allows some
|
||||
// delay for a response message to use the entry (if entry
|
||||
// is used it will be moved to the cached list). If a
|
||||
// snooped entry is not used after the timeout, we allow
|
||||
// it to be evicted. To ensure snooped entries do not
|
||||
// overwrite other cached entries, we limit the number of
|
||||
// snooped entries that are in timeout mode and cannot be
|
||||
// evicted by `kMaxNonEvictableSnoopedEntries`.
|
||||
|
||||
VerifyOrExit(numNonEvictable < kMaxNonEvictableSnoopedEntries);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
MoveCacheEntryAt(aEntry, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t newAge = lastInCachedState + 1;
|
||||
|
||||
if (newAge == kCacheEntries)
|
||||
{
|
||||
newAge = kCacheEntries - 1;
|
||||
}
|
||||
|
||||
MoveCacheEntryAt(aEntry, newAge);
|
||||
}
|
||||
exit:
|
||||
return newEntry;
|
||||
}
|
||||
|
||||
const char *AddressResolver::InvalidationReasonToString(InvalidationReason aReason)
|
||||
{
|
||||
const char *str = "";
|
||||
|
||||
switch (aReason)
|
||||
{
|
||||
case kReasonRemovingRouterId:
|
||||
str = "removing router id";
|
||||
break;
|
||||
|
||||
case kReasonRemovingRloc16:
|
||||
str = "removing rloc16";
|
||||
break;
|
||||
|
||||
case kReasonReceivedIcmpDstUnreachNoRoute:
|
||||
str = "received icmp no route";
|
||||
break;
|
||||
|
||||
case kReasonEvictingForNewEntry:
|
||||
str = "evicting for new entry";
|
||||
break;
|
||||
|
||||
case kReasonRemovingEid:
|
||||
str = "removing eid";
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void AddressResolver::InvalidateCacheEntry(Cache &aEntry, InvalidationReason aReason)
|
||||
void AddressResolver::RemoveCacheEntry(CacheEntry & aEntry,
|
||||
CacheEntryList &aList,
|
||||
CacheEntry * aPrevEntry,
|
||||
Reason aReason)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aReason);
|
||||
|
||||
switch (aEntry.mState)
|
||||
aList.PopAfter(aPrevEntry);
|
||||
|
||||
if (&aList == &mQueryList)
|
||||
{
|
||||
case Cache::kStateCached:
|
||||
otLogNoteArp("Cache entry removed: %s, 0x%04x - %s", aEntry.mTarget.ToString().AsCString(), aEntry.mRloc16,
|
||||
InvalidationReasonToString(aReason));
|
||||
break;
|
||||
|
||||
case Cache::kStateQuery:
|
||||
otLogNoteArp("Cache entry (query mode) removed: %s, timeout:%d, retry:%d - %s",
|
||||
aEntry.mTarget.ToString().AsCString(), aEntry.mTimeout, aEntry.mRetryTimeout,
|
||||
InvalidationReasonToString(aReason));
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(aEntry.mTarget, OT_ERROR_DROP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
Get<MeshForwarder>().HandleResolved(aEntry.GetTarget(), OT_ERROR_DROP);
|
||||
}
|
||||
|
||||
aEntry.mState = Cache::kStateInvalid;
|
||||
MoveCacheEntryAtBack(aEntry);
|
||||
otLogNoteArp("Cache entry removed: %s, 0x%04x (%s) - %s", aEntry.GetTarget().ToString().AsCString(),
|
||||
aEntry.GetRloc16(), ListToString(aList), ReasonToString(aReason));
|
||||
}
|
||||
|
||||
otError AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16)
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
otError error = OT_ERROR_NONE;
|
||||
CacheEntryList *list;
|
||||
CacheEntry * entry;
|
||||
CacheEntry * prev;
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
entry = FindCacheEntry(aEid, list, prev);
|
||||
VerifyOrExit(entry != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
if ((list == &mCachedList) || (list == &mSnoopedList))
|
||||
{
|
||||
if (mCache[i].mState == Cache::kStateInvalid || mCache[i].mTarget != aEid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
VerifyOrExit(entry->GetRloc16() != aRloc16);
|
||||
entry->SetRloc16(aRloc16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Entry is in `mQueryList` or `mQueryRetryList`. Remove it
|
||||
// from its current list, update it, and then add it to the
|
||||
// `mCachedList`.
|
||||
|
||||
if (mCache[i].mRloc16 != aRloc16)
|
||||
{
|
||||
// not updating the age here is intentional because this cache entry is not actually being used
|
||||
mCache[i].mRloc16 = aRloc16;
|
||||
list->PopAfter(prev);
|
||||
|
||||
if (mCache[i].mState != Cache::kStateCached)
|
||||
{
|
||||
mCache[i].mLastTransactionTime = static_cast<uint32_t>(kLastTransactionTimeInvalid);
|
||||
mCache[i].mRetryTimeout = 0;
|
||||
mCache[i].mTimeout = 0;
|
||||
mCache[i].mFailures = 0;
|
||||
mCache[i].mState = Cache::kStateCached;
|
||||
MoveCacheEntryAtFront(mCache[i]);
|
||||
entry->SetRloc16(aRloc16);
|
||||
entry->MarkLastTransactionTimeAsInvalid();
|
||||
mCachedList.Push(*entry);
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(aEid, OT_ERROR_NONE);
|
||||
}
|
||||
|
||||
otLogNoteArp("Cache entry updated (snoop): %s, 0x%04x", aEid.ToString().AsCString(), aRloc16);
|
||||
}
|
||||
|
||||
error = OT_ERROR_NONE;
|
||||
Get<MeshForwarder>().HandleResolved(aEid, OT_ERROR_NONE);
|
||||
}
|
||||
|
||||
otLogNoteArp("Cache entry updated (snoop): %s, 0x%04x", aEid.ToString().AsCString(), aRloc16);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError AddressResolver::AddCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16)
|
||||
otError AddressResolver::AddSnoopedCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Cache * entry = NewCacheEntry();
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint16_t numNonEvictable = 0;
|
||||
CacheEntry *entry;
|
||||
|
||||
entry = NewCacheEntry(/* aSnoopedEntry */ true);
|
||||
VerifyOrExit(entry != NULL, error = OT_ERROR_NO_BUFS);
|
||||
|
||||
entry->mTarget = aEid;
|
||||
entry->mLastTransactionTime = static_cast<uint32_t>(kLastTransactionTimeInvalid);
|
||||
entry->mRloc16 = aRloc16;
|
||||
entry->mRetryTimeout = 0;
|
||||
entry->mTimeout = 0;
|
||||
entry->mFailures = 0;
|
||||
entry->mState = Cache::kStateCached;
|
||||
MoveCacheEntryBehindCached(*entry);
|
||||
for (CacheEntry *snooped = mSnoopedList.GetHead(); snooped != NULL; snooped = snooped->GetNext())
|
||||
{
|
||||
if (!snooped->CanEvict())
|
||||
{
|
||||
numNonEvictable++;
|
||||
}
|
||||
}
|
||||
|
||||
entry->SetTarget(aEid);
|
||||
entry->SetRloc16(aRloc16);
|
||||
|
||||
if (numNonEvictable < kMaxNonEvictableSnoopedEntries)
|
||||
{
|
||||
entry->SetCanEvict(false);
|
||||
entry->SetTimeout(kSnoopBlockEvictionTimeout);
|
||||
|
||||
if (!mTimer.IsRunning())
|
||||
{
|
||||
mTimer.Start(kStateUpdatePeriod);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->SetCanEvict(true);
|
||||
entry->SetTimeout(0);
|
||||
}
|
||||
|
||||
mSnoopedList.Push(*entry);
|
||||
|
||||
otLogNoteArp("Cache entry added (snoop): %s, 0x%04x", aEid.ToString().AsCString(), aRloc16);
|
||||
|
||||
@@ -355,84 +392,101 @@ exit:
|
||||
|
||||
void AddressResolver::RestartAddressQueries(void)
|
||||
{
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
CacheEntry *tail;
|
||||
|
||||
// We move all entries from `mQueryRetryList` at the tail of
|
||||
// `mQueryList` and then (re)send Address Query for all entries in
|
||||
// the updated `mQueryList`.
|
||||
|
||||
tail = mQueryList.GetTail();
|
||||
|
||||
if (tail == NULL)
|
||||
{
|
||||
Cache &entry = mCache[i];
|
||||
mQueryList.SetHead(mQueryRetryList.GetHead());
|
||||
}
|
||||
else
|
||||
{
|
||||
tail->SetNext(mQueryRetryList.GetHead());
|
||||
}
|
||||
|
||||
if (entry.mState != Cache::kStateQuery)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mQueryRetryList.Clear();
|
||||
|
||||
SendAddressQuery(entry.mTarget);
|
||||
for (CacheEntry *entry = mQueryList.GetHead(); entry != NULL; entry = entry->GetNext())
|
||||
{
|
||||
SendAddressQuery(entry->GetTarget());
|
||||
|
||||
entry.mTimeout = kAddressQueryTimeout;
|
||||
entry.mFailures = 0;
|
||||
entry.mRetryTimeout = kAddressQueryInitialRetryDelay;
|
||||
entry->SetTimeout(kAddressQueryTimeout);
|
||||
entry->SetRetryDelay(kAddressQueryInitialRetryDelay);
|
||||
entry->SetCanEvict(false);
|
||||
}
|
||||
}
|
||||
|
||||
otError AddressResolver::Resolve(const Ip6::Address &aEid, uint16_t &aRloc16)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Cache * entry = NULL;
|
||||
otError error = OT_ERROR_NONE;
|
||||
CacheEntry * entry;
|
||||
CacheEntry * prev;
|
||||
CacheEntryList *list;
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (mCache[i].mState != Cache::kStateInvalid)
|
||||
{
|
||||
if (mCache[i].mTarget == aEid)
|
||||
{
|
||||
entry = &mCache[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
entry = FindCacheEntry(aEid, list, prev);
|
||||
|
||||
if (entry == NULL)
|
||||
{
|
||||
entry = NewCacheEntry();
|
||||
// If the entry is not present in any of the lists, try to
|
||||
// allocate a new entry and perform address query. We do not
|
||||
// allow first-time address query entries to be evicted till
|
||||
// timeout.
|
||||
|
||||
entry = NewCacheEntry(/* aSnoopedEntry */ false);
|
||||
VerifyOrExit(entry != NULL, error = OT_ERROR_NO_BUFS);
|
||||
|
||||
entry->SetTarget(aEid);
|
||||
entry->SetRloc16(Mac::kShortAddrInvalid);
|
||||
entry->SetRetryDelay(kAddressQueryInitialRetryDelay);
|
||||
entry->SetCanEvict(false);
|
||||
list = NULL;
|
||||
}
|
||||
|
||||
VerifyOrExit(entry != NULL, error = OT_ERROR_NO_BUFS);
|
||||
|
||||
switch (entry->mState)
|
||||
if ((list == &mCachedList) || (list == &mSnoopedList))
|
||||
{
|
||||
case Cache::kStateInvalid:
|
||||
SuccessOrExit(error = SendAddressQuery(aEid));
|
||||
entry->mTarget = aEid;
|
||||
entry->mRloc16 = Mac::kShortAddrInvalid;
|
||||
entry->mTimeout = kAddressQueryTimeout;
|
||||
entry->mFailures = 0;
|
||||
entry->mRetryTimeout = kAddressQueryInitialRetryDelay;
|
||||
entry->mState = Cache::kStateQuery;
|
||||
error = OT_ERROR_ADDRESS_QUERY;
|
||||
break;
|
||||
// Remove the entry from its current list and push it at the
|
||||
// head of cached list.
|
||||
|
||||
case Cache::kStateQuery:
|
||||
if (entry->mTimeout > 0)
|
||||
list->PopAfter(prev);
|
||||
|
||||
if (list == &mSnoopedList)
|
||||
{
|
||||
error = OT_ERROR_ADDRESS_QUERY;
|
||||
}
|
||||
else if (entry->mTimeout == 0 && entry->mRetryTimeout == 0)
|
||||
{
|
||||
SuccessOrExit(error = SendAddressQuery(aEid));
|
||||
entry->mTimeout = kAddressQueryTimeout;
|
||||
error = OT_ERROR_ADDRESS_QUERY;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_DROP;
|
||||
entry->MarkLastTransactionTimeAsInvalid();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Cache::kStateCached:
|
||||
aRloc16 = entry->mRloc16;
|
||||
MoveCacheEntryAtFront(*entry);
|
||||
break;
|
||||
mCachedList.Push(*entry);
|
||||
aRloc16 = entry->GetRloc16();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
if (list == &mQueryList)
|
||||
{
|
||||
ExitNow(error = OT_ERROR_ADDRESS_QUERY);
|
||||
}
|
||||
|
||||
if (list == &mQueryRetryList)
|
||||
{
|
||||
// Allow an entry in query-retry mode to resend an Address
|
||||
// Query again only if the timeout (retry delay interval) is
|
||||
// expired.
|
||||
|
||||
VerifyOrExit(entry->IsTimeoutZero(), error = OT_ERROR_DROP);
|
||||
mQueryRetryList.PopAfter(prev);
|
||||
}
|
||||
|
||||
entry->SetTimeout(kAddressQueryTimeout);
|
||||
|
||||
error = SendAddressQuery(aEid);
|
||||
VerifyOrExit(error == OT_ERROR_NONE, mUnusedList.Push(*entry));
|
||||
|
||||
mQueryList.Push(*entry);
|
||||
error = OT_ERROR_ADDRESS_QUERY;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
@@ -483,10 +537,13 @@ void AddressResolver::HandleAddressNotification(void *aContext, otMessage *aMess
|
||||
|
||||
void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
Ip6::Address target;
|
||||
uint8_t meshLocalIid[Ip6::Address::kInterfaceIdentifierSize];
|
||||
uint16_t rloc16;
|
||||
uint32_t lastTransactionTime;
|
||||
Ip6::Address target;
|
||||
uint8_t meshLocalIid[Ip6::Address::kInterfaceIdentifierSize];
|
||||
uint16_t rloc16;
|
||||
uint32_t lastTransactionTime;
|
||||
CacheEntryList *list;
|
||||
CacheEntry * entry;
|
||||
CacheEntry * prev;
|
||||
|
||||
VerifyOrExit(aMessage.IsConfirmable() && aMessage.GetCode() == OT_COAP_CODE_POST);
|
||||
|
||||
@@ -508,58 +565,41 @@ void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const I
|
||||
otLogInfoArp("Received address notification from 0x%04x for %s to 0x%04x", aMessageInfo.GetPeerAddr().GetLocator(),
|
||||
target.ToString().AsCString(), rloc16);
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
entry = FindCacheEntry(target, list, prev);
|
||||
VerifyOrExit(entry != NULL);
|
||||
|
||||
if (list == &mCachedList)
|
||||
{
|
||||
if (mCache[i].mTarget != target)
|
||||
if (entry->IsLastTransactionTimeValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Receiving multiple Address Notification for an EID from
|
||||
// different mesh-local IIDs indicates address is in use
|
||||
// by more than one device. Try to resolve the duplicate
|
||||
// address by sending an Address Error message.
|
||||
|
||||
switch (mCache[i].mState)
|
||||
{
|
||||
case Cache::kStateInvalid:
|
||||
break;
|
||||
VerifyOrExit(entry->HasMeshLocalIid(meshLocalIid), SendAddressError(target, meshLocalIid, NULL));
|
||||
|
||||
case Cache::kStateCached:
|
||||
if (mCache[i].mLastTransactionTime != kLastTransactionTimeInvalid)
|
||||
{
|
||||
if (memcmp(mCache[i].mMeshLocalIid, meshLocalIid, sizeof(mCache[i].mMeshLocalIid)) != 0)
|
||||
{
|
||||
SendAddressError(target, meshLocalIid, NULL);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
if (lastTransactionTime >= mCache[i].mLastTransactionTime)
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
// fall through
|
||||
|
||||
case Cache::kStateQuery:
|
||||
memcpy(mCache[i].mMeshLocalIid, meshLocalIid, sizeof(mCache[i].mMeshLocalIid));
|
||||
mCache[i].mRloc16 = rloc16;
|
||||
mCache[i].mRetryTimeout = 0;
|
||||
mCache[i].mLastTransactionTime = lastTransactionTime;
|
||||
mCache[i].mTimeout = 0;
|
||||
mCache[i].mFailures = 0;
|
||||
mCache[i].mState = Cache::kStateCached;
|
||||
MoveCacheEntryAtFront(mCache[i]);
|
||||
|
||||
otLogNoteArp("Cache entry updated (notification): %s, 0x%04x, lastTrans:%d", target.ToString().AsCString(),
|
||||
rloc16, lastTransactionTime);
|
||||
|
||||
if (Get<Coap::Coap>().SendEmptyAck(aMessage, aMessageInfo) == OT_ERROR_NONE)
|
||||
{
|
||||
otLogInfoArp("Sending address notification acknowledgment");
|
||||
}
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(target, OT_ERROR_NONE);
|
||||
break;
|
||||
VerifyOrExit(lastTransactionTime < entry->GetLastTransactionTime());
|
||||
}
|
||||
}
|
||||
|
||||
entry->SetRloc16(rloc16);
|
||||
entry->SetMeshLocalIid(meshLocalIid);
|
||||
entry->SetLastTransactionTime(lastTransactionTime);
|
||||
|
||||
list->PopAfter(prev);
|
||||
mCachedList.Push(*entry);
|
||||
|
||||
otLogNoteArp("Cache entry updated (notification): %s, 0x%04x, lastTrans:%d", target.ToString().AsCString(), rloc16,
|
||||
lastTransactionTime);
|
||||
|
||||
if (Get<Coap::Coap>().SendEmptyAck(aMessage, aMessageInfo) == OT_ERROR_NONE)
|
||||
{
|
||||
otLogInfoArp("Sending address notification acknowledgment");
|
||||
}
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(target, OT_ERROR_NONE);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
@@ -786,44 +826,71 @@ void AddressResolver::HandleTimer(Timer &aTimer)
|
||||
|
||||
void AddressResolver::HandleTimer(void)
|
||||
{
|
||||
bool continueTimer = false;
|
||||
bool continueTimer = false;
|
||||
CacheEntry *prev;
|
||||
CacheEntry *entry;
|
||||
CacheEntry *next;
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
for (entry = mSnoopedList.GetHead(); entry != NULL; entry = entry->GetNext())
|
||||
{
|
||||
if (mCache[i].mState != Cache::kStateQuery)
|
||||
if (entry->IsTimeoutZero())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
continueTimer = true;
|
||||
entry->DecrementTimeout();
|
||||
|
||||
if (mCache[i].mTimeout > 0)
|
||||
if (entry->IsTimeoutZero())
|
||||
{
|
||||
mCache[i].mTimeout--;
|
||||
|
||||
if (mCache[i].mTimeout == 0)
|
||||
{
|
||||
mCache[i].mRetryTimeout =
|
||||
static_cast<uint16_t>(kAddressQueryInitialRetryDelay * (1 << mCache[i].mFailures));
|
||||
|
||||
if (mCache[i].mRetryTimeout < kAddressQueryMaxRetryDelay)
|
||||
{
|
||||
mCache[i].mFailures++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCache[i].mRetryTimeout = kAddressQueryMaxRetryDelay;
|
||||
}
|
||||
|
||||
otLogInfoArp("Timed out waiting for address notification for %s, retry: %d",
|
||||
mCache[i].mTarget.ToString().AsCString(), mCache[i].mRetryTimeout);
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(mCache[i].mTarget, OT_ERROR_DROP);
|
||||
}
|
||||
entry->SetCanEvict(true);
|
||||
}
|
||||
else if (mCache[i].mRetryTimeout > 0)
|
||||
}
|
||||
|
||||
for (entry = mQueryRetryList.GetHead(); entry != NULL; entry = entry->GetNext())
|
||||
{
|
||||
if (entry->IsTimeoutZero())
|
||||
{
|
||||
mCache[i].mRetryTimeout--;
|
||||
continue;
|
||||
}
|
||||
|
||||
continueTimer = true;
|
||||
entry->DecrementTimeout();
|
||||
}
|
||||
|
||||
for (prev = NULL, entry = mQueryList.GetHead(); entry != NULL; entry = next)
|
||||
{
|
||||
next = entry->GetNext();
|
||||
|
||||
OT_ASSERT(!entry->IsTimeoutZero());
|
||||
|
||||
continueTimer = true;
|
||||
entry->DecrementTimeout();
|
||||
|
||||
if (entry->IsTimeoutZero())
|
||||
{
|
||||
uint16_t retryDelay = entry->GetRetryDelay();
|
||||
|
||||
entry->SetTimeout(retryDelay);
|
||||
|
||||
retryDelay <<= 1;
|
||||
|
||||
if (retryDelay > kAddressQueryMaxRetryDelay)
|
||||
{
|
||||
retryDelay = kAddressQueryMaxRetryDelay;
|
||||
}
|
||||
|
||||
entry->SetRetryDelay(retryDelay);
|
||||
entry->SetCanEvict(true);
|
||||
|
||||
// Move the entry from `mQueryList` to `mQueryRetryList`
|
||||
mQueryList.PopAfter(prev);
|
||||
mQueryRetryList.Push(*entry);
|
||||
|
||||
otLogInfoArp("Timed out waiting for address notification for %s, retry: %d",
|
||||
entry->GetTarget().ToString().AsCString(), entry->GetTimeout());
|
||||
|
||||
Get<MeshForwarder>().HandleResolved(entry->GetTarget(), OT_ERROR_DROP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,19 +924,98 @@ void AddressResolver::HandleIcmpReceive(Message & aMessage,
|
||||
VerifyOrExit(aIcmpHeader.GetCode() == Ip6::IcmpHeader::kCodeDstUnreachNoRoute);
|
||||
VerifyOrExit(aMessage.Read(aMessage.GetOffset(), sizeof(ip6Header), &ip6Header) == sizeof(ip6Header));
|
||||
|
||||
for (int i = 0; i < kCacheEntries; i++)
|
||||
{
|
||||
if (mCache[i].mState != Cache::kStateInvalid && mCache[i].mTarget == ip6Header.GetDestination())
|
||||
{
|
||||
InvalidateCacheEntry(mCache[i], kReasonReceivedIcmpDstUnreachNoRoute);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Remove(ip6Header.GetDestination(), kReasonReceivedIcmpDstUnreachNoRoute);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
// LCOV_EXCL_START
|
||||
|
||||
const char *AddressResolver::ReasonToString(Reason aReason)
|
||||
{
|
||||
const char *str = "";
|
||||
|
||||
switch (aReason)
|
||||
{
|
||||
case kReasonRemovingRouterId:
|
||||
str = "removing router id";
|
||||
break;
|
||||
|
||||
case kReasonRemovingRloc16:
|
||||
str = "removing rloc16";
|
||||
break;
|
||||
|
||||
case kReasonReceivedIcmpDstUnreachNoRoute:
|
||||
str = "received icmp no route";
|
||||
break;
|
||||
|
||||
case kReasonEvictingForNewEntry:
|
||||
str = "evicting for new entry";
|
||||
break;
|
||||
|
||||
case kReasonRemovingEid:
|
||||
str = "removing eid";
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
const char *AddressResolver::ListToString(const CacheEntryList &aList) const
|
||||
{
|
||||
const char *str;
|
||||
|
||||
VerifyOrExit(&aList != &mCachedList, str = "cached");
|
||||
VerifyOrExit(&aList != &mSnoopedList, str = "snooped");
|
||||
VerifyOrExit(&aList != &mQueryList, str = "query");
|
||||
VerifyOrExit(&aList != &mQueryRetryList, str = "query-retry");
|
||||
str = "unused";
|
||||
|
||||
exit:
|
||||
return str;
|
||||
}
|
||||
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// AddressResolver::CacheEntry
|
||||
|
||||
void AddressResolver::CacheEntry::Init(Instance &aInstance)
|
||||
{
|
||||
InstanceLocatorInit::Init(aInstance);
|
||||
mNextIndex = kNoNextIndex;
|
||||
}
|
||||
|
||||
AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void)
|
||||
{
|
||||
return (mNextIndex == kNoNextIndex) ? NULL : &Get<AddressResolver>().mCacheEntries[mNextIndex];
|
||||
}
|
||||
|
||||
const AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) const
|
||||
{
|
||||
return (mNextIndex == kNoNextIndex) ? NULL : &Get<AddressResolver>().mCacheEntries[mNextIndex];
|
||||
}
|
||||
|
||||
void AddressResolver::CacheEntry::SetNext(CacheEntry *aEntry)
|
||||
{
|
||||
VerifyOrExit(aEntry != NULL, mNextIndex = kNoNextIndex);
|
||||
mNextIndex = static_cast<uint16_t>(aEntry - Get<AddressResolver>().mCacheEntries);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool AddressResolver::CacheEntry::HasMeshLocalIid(const uint8_t *aIid) const
|
||||
{
|
||||
return memcmp(mInfo.mCached.mMeshLocalIid, aIid, Ip6::Address::kInterfaceIdentifierSize) == 0;
|
||||
}
|
||||
|
||||
void AddressResolver::CacheEntry::SetMeshLocalIid(const uint8_t *aIid)
|
||||
{
|
||||
memcpy(mInfo.mCached.mMeshLocalIid, aIid, Ip6::Address::kInterfaceIdentifierSize);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include "coap/coap.hpp"
|
||||
#include "common/linked_list.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "mac/mac.hpp"
|
||||
@@ -92,7 +93,7 @@ public:
|
||||
* @param[in] aRloc16 The RLOC16 address.
|
||||
*
|
||||
*/
|
||||
void Remove(uint16_t aRloc16);
|
||||
void Remove(Mac::ShortAddress aRloc16);
|
||||
|
||||
/**
|
||||
* This method removes all EID-to-RLOC cache entries associated with a Router ID.
|
||||
@@ -123,16 +124,19 @@ public:
|
||||
otError UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16);
|
||||
|
||||
/**
|
||||
* This method adds one cache entry for the EID.
|
||||
* This method adds a snooped cache entry for a given EID.
|
||||
*
|
||||
* The method is intended to add an entry for snoop optimization (inspection of a received message to create a
|
||||
* cache entry mapping an EID to a RLOC).
|
||||
*
|
||||
* @param[in] aEid A reference to the EID.
|
||||
* @param[in] aRloc16 The RLOC16 corresponding to @p aEid.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully adds one cache entry.
|
||||
* @retval OT_ERROR_NONE Successfully adds the cache entry.
|
||||
* @retval OT_ERROR_NO_BUFS Insufficient buffer space available to add one cache entry.
|
||||
*
|
||||
*/
|
||||
otError AddCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16);
|
||||
otError AddSnoopedCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16);
|
||||
|
||||
/**
|
||||
* This method returns the RLOC16 for a given EID, or initiates an Address Query if the mapping is not known.
|
||||
@@ -159,47 +163,81 @@ public:
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kCacheEntries = OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES,
|
||||
kStateUpdatePeriod = 1000u, ///< State update period in milliseconds.
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread Protocol Parameters and Constants
|
||||
*
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kCacheEntries = OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES,
|
||||
kMaxNonEvictableSnoopedEntries = OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES,
|
||||
kAddressQueryTimeout = OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT, // in seconds
|
||||
kAddressQueryInitialRetryDelay = OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY, // in seconds
|
||||
kAddressQueryMaxRetryDelay = OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY, // in seconds
|
||||
kSnoopBlockEvictionTimeout = OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT, // in seconds
|
||||
kStateUpdatePeriod = 1000u, // in milliseconds
|
||||
};
|
||||
|
||||
enum
|
||||
class CacheEntry : public InstanceLocatorInit
|
||||
{
|
||||
kLastTransactionTimeInvalid = 0xffffffff, ///< Used when entry is populated using forwarded data message.
|
||||
};
|
||||
public:
|
||||
void Init(Instance &aInstance);
|
||||
|
||||
struct Cache
|
||||
{
|
||||
enum State
|
||||
CacheEntry * GetNext(void);
|
||||
const CacheEntry *GetNext(void) const;
|
||||
void SetNext(CacheEntry *aEntry);
|
||||
|
||||
const Ip6::Address &GetTarget(void) const { return mTarget; }
|
||||
void SetTarget(const Ip6::Address &aTarget) { mTarget = aTarget; }
|
||||
|
||||
Mac::ShortAddress GetRloc16(void) const { return mRloc16; }
|
||||
void SetRloc16(Mac::ShortAddress aRloc16) { mRloc16 = aRloc16; }
|
||||
|
||||
const uint8_t *GetMeshLocalIid(void) const { return mInfo.mCached.mMeshLocalIid; }
|
||||
bool HasMeshLocalIid(const uint8_t *aIid) const;
|
||||
void SetMeshLocalIid(const uint8_t *aIid);
|
||||
|
||||
uint32_t GetLastTransactionTime(void) const { return mInfo.mCached.mLastTransactionTime; }
|
||||
void SetLastTransactionTime(uint32_t aTime) { mInfo.mCached.mLastTransactionTime = aTime; }
|
||||
bool IsLastTransactionTimeValid(void) const { return GetLastTransactionTime() != kInvalidLastTransTime; }
|
||||
void MarkLastTransactionTimeAsInvalid(void) { SetLastTransactionTime(kInvalidLastTransTime); }
|
||||
|
||||
void DecrementTimeout(void) { mInfo.mOther.mTimeout--; }
|
||||
bool IsTimeoutZero(void) const { return mInfo.mOther.mTimeout == 0; }
|
||||
uint16_t GetTimeout(void) const { return mInfo.mOther.mTimeout; }
|
||||
void SetTimeout(uint16_t aTimeout) { mInfo.mOther.mTimeout = aTimeout; }
|
||||
|
||||
uint16_t GetRetryDelay(void) const { return mInfo.mOther.mRetryDelay; }
|
||||
void SetRetryDelay(uint16_t aDelay) { mInfo.mOther.mRetryDelay = aDelay; }
|
||||
|
||||
bool CanEvict(void) const { return mInfo.mOther.mCanEvict; }
|
||||
void SetCanEvict(bool aCanEvict) { mInfo.mOther.mCanEvict = aCanEvict; }
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kStateInvalid,
|
||||
kStateQuery,
|
||||
kStateCached,
|
||||
kNoNextIndex = 0xffff, // mNextIndex value when at end of list.
|
||||
kInvalidLastTransTime = 0xffffffff, // Value indicating mLastTransactionTime is invalid.
|
||||
};
|
||||
|
||||
Ip6::Address mTarget;
|
||||
uint8_t mMeshLocalIid[Ip6::Address::kInterfaceIdentifierSize];
|
||||
uint32_t mLastTransactionTime;
|
||||
Mac::ShortAddress mRloc16;
|
||||
uint16_t mRetryTimeout;
|
||||
uint8_t mTimeout;
|
||||
uint8_t mFailures;
|
||||
uint8_t mAge;
|
||||
State mState;
|
||||
uint16_t mNextIndex;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t mLastTransactionTime;
|
||||
uint8_t mMeshLocalIid[Ip6::Address::kInterfaceIdentifierSize];
|
||||
} mCached;
|
||||
|
||||
struct
|
||||
{
|
||||
uint16_t mTimeout;
|
||||
uint16_t mRetryDelay;
|
||||
bool mCanEvict;
|
||||
} mOther;
|
||||
|
||||
} mInfo;
|
||||
};
|
||||
|
||||
enum InvalidationReason
|
||||
typedef LinkedList<CacheEntry> CacheEntryList;
|
||||
|
||||
enum Reason
|
||||
{
|
||||
kReasonRemovingRouterId,
|
||||
kReasonRemovingRloc16,
|
||||
@@ -208,17 +246,12 @@ private:
|
||||
kReasonRemovingEid,
|
||||
};
|
||||
|
||||
static const char *InvalidationReasonToString(InvalidationReason aReason);
|
||||
|
||||
void Init(void);
|
||||
Cache *NewCacheEntry(void);
|
||||
|
||||
void MoveCacheEntryAt(Cache &aEntry, uint8_t aAge);
|
||||
void MoveCacheEntryAtFront(Cache &aEntry) { MoveCacheEntryAt(aEntry, 0); }
|
||||
void MoveCacheEntryAtBack(Cache &aEntry) { MoveCacheEntryAt(aEntry, kCacheEntries - 1); }
|
||||
void MoveCacheEntryBehindCached(Cache &aEntry);
|
||||
|
||||
void InvalidateCacheEntry(Cache &aEntry, InvalidationReason aReason);
|
||||
void Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId);
|
||||
void Remove(const Ip6::Address &aEid, Reason aReason);
|
||||
CacheEntry *FindCacheEntryInList(CacheEntryList &aList, const Ip6::Address &aEid, CacheEntry *&aPrevEntry);
|
||||
CacheEntry *FindCacheEntry(const Ip6::Address &aEid, CacheEntryList *&aList, CacheEntry *&aPrevEntry);
|
||||
CacheEntry *NewCacheEntry(bool aSnoopedEntry);
|
||||
void RemoveCacheEntry(CacheEntry &aEntry, CacheEntryList &aList, CacheEntry *aPrevEntry, Reason aReason);
|
||||
|
||||
otError SendAddressQuery(const Ip6::Address &aEid);
|
||||
otError SendAddressError(const Ip6::Address &aTarget,
|
||||
@@ -249,10 +282,20 @@ private:
|
||||
static void HandleTimer(Timer &aTimer);
|
||||
void HandleTimer(void);
|
||||
|
||||
Coap::Resource mAddressError;
|
||||
Coap::Resource mAddressQuery;
|
||||
Coap::Resource mAddressNotification;
|
||||
Cache mCache[kCacheEntries];
|
||||
const char * ListToString(const CacheEntryList &aList) const;
|
||||
static const char *ReasonToString(Reason aReason);
|
||||
|
||||
Coap::Resource mAddressError;
|
||||
Coap::Resource mAddressQuery;
|
||||
Coap::Resource mAddressNotification;
|
||||
|
||||
CacheEntry mCacheEntries[kCacheEntries];
|
||||
CacheEntryList mCachedList;
|
||||
CacheEntryList mSnoopedList;
|
||||
CacheEntryList mQueryList;
|
||||
CacheEntryList mQueryRetryList;
|
||||
CacheEntryList mUnusedList;
|
||||
|
||||
Ip6::IcmpHandler mIcmpHandler;
|
||||
TimerMilli mTimer;
|
||||
};
|
||||
|
||||
@@ -613,7 +613,7 @@ void MeshForwarder::UpdateRoutes(uint8_t * aFrame,
|
||||
(aMeshDest.GetShort() == Get<Mac::Mac>().GetShortAddress() ||
|
||||
Get<Mle::MleRouter>().IsMinimalChild(aMeshDest.GetShort())))
|
||||
{
|
||||
Get<AddressResolver>().AddCacheEntry(ip6Header.GetSource(), aMeshSource.GetShort());
|
||||
Get<AddressResolver>().AddSnoopedCacheEntry(ip6Header.GetSource(), aMeshSource.GetShort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user