blob: 11e2adc866f02254735f5bdaa68e3527528d8c41 [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements Thread's EID-to-RLOC mapping and caching.
*/
#if OPENTHREAD_FTD
#include "address_resolver.hpp"
#include "coap/coap_message.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "mac/mac_types.hpp"
#include "thread/mesh_forwarder.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_uri_paths.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
AddressResolver::AddressResolver(Instance &aInstance)
: InstanceLocator(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)
, mIcmpHandler(&AddressResolver::HandleIcmpReceive, this)
, mTimer(aInstance, &AddressResolver::HandleTimer, this)
{
Clear();
Get<Coap::Coap>().AddResource(mAddressError);
Get<Coap::Coap>().AddResource(mAddressQuery);
Get<Coap::Coap>().AddResource(mAddressNotification);
Get<Ip6::Icmp>().RegisterHandler(mIcmpHandler);
}
void AddressResolver::Clear(void)
{
memset(&mCache, 0, sizeof(mCache));
for (uint8_t i = 0; i < kCacheEntries; i++)
{
mCache[i].mAge = i;
}
}
otError AddressResolver::GetEntry(uint8_t aIndex, otEidCacheEntry &aEntry) const
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aIndex < kCacheEntries, error = OT_ERROR_INVALID_ARGS);
aEntry.mTarget = mCache[aIndex].mTarget;
aEntry.mRloc16 = mCache[aIndex].mRloc16;
aEntry.mAge = mCache[aIndex].mAge;
aEntry.mValid = mCache[aIndex].mState == Cache::kStateCached;
exit:
return error;
}
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);
}
}
}
void AddressResolver::Remove(uint16_t aRloc16)
{
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mRloc16 == aRloc16)
{
InvalidateCacheEntry(mCache[i], kReasonRemovingRloc16);
}
}
}
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;
}
}
AddressResolver::Cache *AddressResolver::NewCacheEntry(void)
{
Cache *rval = NULL;
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mState == Cache::kStateQuery && mCache[i].mFailures == 0)
{
continue;
}
if (rval == NULL || rval->mAge < mCache[i].mAge)
{
rval = &mCache[i];
}
}
if (rval != NULL)
{
InvalidateCacheEntry(*rval, kReasonEvictingForNewEntry);
}
return rval;
}
void AddressResolver::MarkCacheEntryAsUsed(Cache &aEntry)
{
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mAge < aEntry.mAge)
{
mCache[i].mAge++;
}
}
aEntry.mAge = 0;
}
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)
{
OT_UNUSED_VARIABLE(aReason);
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mAge > aEntry.mAge)
{
mCache[i].mAge--;
}
}
switch (aEntry.mState)
{
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));
break;
default:
break;
}
aEntry.mAge = kCacheEntries - 1;
aEntry.mState = Cache::kStateInvalid;
}
otError AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16)
{
otError error = OT_ERROR_NOT_FOUND;
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mState == Cache::kStateInvalid || mCache[i].mTarget != aEid)
{
continue;
}
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;
if (mCache[i].mState != Cache::kStateCached)
{
mCache[i].mRetryTimeout = 0;
mCache[i].mLastTransactionTime = static_cast<uint32_t>(kLastTransactionTimeInvalid);
mCache[i].mTimeout = 0;
mCache[i].mFailures = 0;
mCache[i].mState = Cache::kStateCached;
Get<MeshForwarder>().HandleResolved(aEid, OT_ERROR_NONE);
}
otLogNoteArp("Cache entry updated (snoop): %s, 0x%04x", aEid.ToString().AsCString(), aRloc16);
}
error = OT_ERROR_NONE;
}
return error;
}
otError AddressResolver::AddCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16)
{
otError error = OT_ERROR_NONE;
Cache * entry = NewCacheEntry();
VerifyOrExit(entry != NULL, error = OT_ERROR_NO_BUFS);
entry->mTarget = aEid;
entry->mRloc16 = aRloc16;
entry->mTimeout = 0;
entry->mFailures = 0;
entry->mState = Cache::kStateCached;
MarkCacheEntryAsUsed(*entry);
exit:
return error;
}
void AddressResolver::RestartAddressQueries(void)
{
for (int i = 0; i < kCacheEntries; i++)
{
Cache &entry = mCache[i];
if (entry.mState != Cache::kStateQuery)
{
continue;
}
SendAddressQuery(entry.mTarget);
entry.mTimeout = kAddressQueryTimeout;
entry.mFailures = 0;
entry.mRetryTimeout = kAddressQueryInitialRetryDelay;
}
}
otError AddressResolver::Resolve(const Ip6::Address &aEid, uint16_t &aRloc16)
{
otError error = OT_ERROR_NONE;
Cache * entry = NULL;
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mState != Cache::kStateInvalid)
{
if (mCache[i].mTarget == aEid)
{
entry = &mCache[i];
break;
}
}
}
if (entry == NULL)
{
entry = NewCacheEntry();
}
VerifyOrExit(entry != NULL, error = OT_ERROR_NO_BUFS);
switch (entry->mState)
{
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;
case Cache::kStateQuery:
if (entry->mTimeout > 0)
{
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;
}
break;
case Cache::kStateCached:
aRloc16 = entry->mRloc16;
MarkCacheEntryAsUsed(*entry);
break;
}
exit:
return error;
}
otError AddressResolver::SendAddressQuery(const Ip6::Address &aEid)
{
otError error;
Coap::Message * message;
ThreadTargetTlv targetTlv;
Ip6::MessageInfo messageInfo;
VerifyOrExit((message = Get<Coap::Coap>().NewMessage()) != NULL, error = OT_ERROR_NO_BUFS);
message->Init(OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
SuccessOrExit(error = message->AppendUriPathOptions(OT_URI_PATH_ADDRESS_QUERY));
SuccessOrExit(error = message->SetPayloadMarker());
targetTlv.Init();
targetTlv.SetTarget(aEid);
SuccessOrExit(error = targetTlv.AppendTo(*message));
messageInfo.GetPeerAddr().mFields.m16[0] = HostSwap16(0xff03);
messageInfo.GetPeerAddr().mFields.m16[7] = HostSwap16(0x0002);
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
otLogInfoArp("Sending address query for %s", aEid.ToString().AsCString());
exit:
if (!mTimer.IsRunning())
{
mTimer.Start(kStateUpdatePeriod);
}
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
void AddressResolver::HandleAddressNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<AddressResolver *>(aContext)->HandleAddressNotification(
*static_cast<Coap::Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
ThreadTargetTlv targetTlv;
ThreadMeshLocalEidTlv mlIidTlv;
ThreadRloc16Tlv rloc16Tlv;
ThreadLastTransactionTimeTlv lastTransactionTimeTlv;
uint32_t lastTransactionTime;
VerifyOrExit(aMessage.GetType() == OT_COAP_TYPE_CONFIRMABLE && aMessage.GetCode() == OT_COAP_CODE_POST);
SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kTarget, sizeof(targetTlv), targetTlv));
VerifyOrExit(targetTlv.IsValid());
targetTlv.Init(); // reset TLV length
SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kMeshLocalEid, sizeof(mlIidTlv), mlIidTlv));
VerifyOrExit(mlIidTlv.IsValid());
mlIidTlv.Init(); // reset TLV length
SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kRloc16, sizeof(rloc16Tlv), rloc16Tlv));
VerifyOrExit(rloc16Tlv.IsValid());
lastTransactionTime = 0;
if (ThreadTlv::GetTlv(aMessage, ThreadTlv::kLastTransactionTime, sizeof(lastTransactionTimeTlv),
lastTransactionTimeTlv) == OT_ERROR_NONE)
{
VerifyOrExit(lastTransactionTimeTlv.IsValid());
lastTransactionTime = lastTransactionTimeTlv.GetTime();
}
otLogInfoArp("Received address notification from 0x%04x for %s to 0x%04x",
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]), targetTlv.GetTarget().ToString().AsCString(),
rloc16Tlv.GetRloc16());
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mTarget != targetTlv.GetTarget())
{
continue;
}
switch (mCache[i].mState)
{
case Cache::kStateInvalid:
break;
case Cache::kStateCached:
if (mCache[i].mLastTransactionTime != kLastTransactionTimeInvalid)
{
if (memcmp(mCache[i].mMeshLocalIid, mlIidTlv.GetIid(), sizeof(mCache[i].mMeshLocalIid)) != 0)
{
SendAddressError(targetTlv, mlIidTlv, NULL);
ExitNow();
}
if (lastTransactionTime >= mCache[i].mLastTransactionTime)
{
ExitNow();
}
}
// fall through
case Cache::kStateQuery:
memcpy(mCache[i].mMeshLocalIid, mlIidTlv.GetIid(), sizeof(mCache[i].mMeshLocalIid));
mCache[i].mRloc16 = rloc16Tlv.GetRloc16();
mCache[i].mRetryTimeout = 0;
mCache[i].mLastTransactionTime = lastTransactionTime;
mCache[i].mTimeout = 0;
mCache[i].mFailures = 0;
mCache[i].mState = Cache::kStateCached;
MarkCacheEntryAsUsed(mCache[i]);
otLogNoteArp("Cache entry updated (notification): %s, 0x%04x, lastTrans:%d",
targetTlv.GetTarget().ToString().AsCString(), rloc16Tlv.GetRloc16(), lastTransactionTime);
if (Get<Coap::Coap>().SendEmptyAck(aMessage, aMessageInfo) == OT_ERROR_NONE)
{
otLogInfoArp("Sending address notification acknowledgment");
}
Get<MeshForwarder>().HandleResolved(targetTlv.GetTarget(), OT_ERROR_NONE);
break;
}
}
exit:
return;
}
otError AddressResolver::SendAddressError(const ThreadTargetTlv & aTarget,
const ThreadMeshLocalEidTlv &aEid,
const Ip6::Address * aDestination)
{
otError error;
Coap::Message * message;
Ip6::MessageInfo messageInfo;
VerifyOrExit((message = Get<Coap::Coap>().NewMessage()) != NULL, error = OT_ERROR_NO_BUFS);
message->Init(aDestination == NULL ? OT_COAP_TYPE_NON_CONFIRMABLE : OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
SuccessOrExit(error = message->AppendUriPathOptions(OT_URI_PATH_ADDRESS_ERROR));
SuccessOrExit(error = message->SetPayloadMarker());
SuccessOrExit(error = aTarget.AppendTo(*message));
SuccessOrExit(error = aEid.AppendTo(*message));
if (aDestination == NULL)
{
messageInfo.GetPeerAddr().mFields.m16[0] = HostSwap16(0xff03);
messageInfo.GetPeerAddr().mFields.m16[7] = HostSwap16(0x0002);
}
else
{
messageInfo.SetPeerAddr(*aDestination);
}
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
otLogInfoArp("Sending address error for target %s", aTarget.GetTarget().ToString().AsCString());
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
void AddressResolver::HandleAddressError(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<AddressResolver *>(aContext)->HandleAddressError(*static_cast<Coap::Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void AddressResolver::HandleAddressError(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
otError error = OT_ERROR_NONE;
ThreadTargetTlv targetTlv;
ThreadMeshLocalEidTlv mlIidTlv;
Mac::ExtAddress macAddr;
Ip6::Address destination;
VerifyOrExit(aMessage.GetType() == OT_COAP_TYPE_CONFIRMABLE && aMessage.GetCode() == OT_COAP_CODE_POST,
error = OT_ERROR_DROP);
otLogInfoArp("Received address error notification");
if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
{
if (Get<Coap::Coap>().SendEmptyAck(aMessage, aMessageInfo) == OT_ERROR_NONE)
{
otLogInfoArp("Sent address error notification acknowledgment");
}
}
SuccessOrExit(error = ThreadTlv::GetTlv(aMessage, ThreadTlv::kTarget, sizeof(targetTlv), targetTlv));
VerifyOrExit(targetTlv.IsValid(), error = OT_ERROR_PARSE);
targetTlv.Init(); // reset TLV length
SuccessOrExit(error = ThreadTlv::GetTlv(aMessage, ThreadTlv::kMeshLocalEid, sizeof(mlIidTlv), mlIidTlv));
VerifyOrExit(mlIidTlv.IsValid(), error = OT_ERROR_PARSE);
mlIidTlv.Init(); // reset TLV length
for (const Ip6::NetifUnicastAddress *address = Get<ThreadNetif>().GetUnicastAddresses(); address;
address = address->GetNext())
{
if (address->GetAddress() == targetTlv.GetTarget() &&
memcmp(Get<Mle::MleRouter>().GetMeshLocal64().GetIid(), mlIidTlv.GetIid(), 8))
{
// Target EID matches address and Mesh Local EID differs
Get<ThreadNetif>().RemoveUnicastAddress(*address);
ExitNow();
}
}
macAddr.Set(mlIidTlv.GetIid());
macAddr.ToggleLocal();
for (ChildTable::Iterator iter(GetInstance(), Child::kInStateValid); !iter.IsDone(); iter++)
{
Child &child = *iter.GetChild();
if (child.IsFullThreadDevice())
{
continue;
}
if (child.GetExtAddress() != macAddr)
{
// Mesh Local EID differs, so check whether Target EID
// matches a child address and if so remove it.
if (child.RemoveIp6Address(GetInstance(), targetTlv.GetTarget()) == OT_ERROR_NONE)
{
destination.Clear();
destination.mFields.m16[0] = HostSwap16(0xfe80);
destination.SetIid(child.GetExtAddress());
SendAddressError(targetTlv, mlIidTlv, &destination);
ExitNow();
}
}
}
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnArp("Error while processing address error notification: %s", otThreadErrorToString(error));
}
}
void AddressResolver::HandleAddressQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
static_cast<AddressResolver *>(aContext)->HandleAddressQuery(*static_cast<Coap::Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void AddressResolver::HandleAddressQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
ThreadTargetTlv targetTlv;
ThreadMeshLocalEidTlv mlIidTlv;
ThreadLastTransactionTimeTlv lastTransactionTimeTlv;
VerifyOrExit(aMessage.GetType() == OT_COAP_TYPE_NON_CONFIRMABLE && aMessage.GetCode() == OT_COAP_CODE_POST);
SuccessOrExit(ThreadTlv::GetTlv(aMessage, ThreadTlv::kTarget, sizeof(targetTlv), targetTlv));
VerifyOrExit(targetTlv.IsValid());
targetTlv.Init(); // reset TLV length
mlIidTlv.Init();
lastTransactionTimeTlv.Init();
otLogInfoArp("Received address query from 0x%04x for target %s",
HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]), targetTlv.GetTarget().ToString().AsCString());
if (Get<ThreadNetif>().IsUnicastAddress(targetTlv.GetTarget()))
{
mlIidTlv.SetIid(Get<Mle::MleRouter>().GetMeshLocal64().GetIid());
SendAddressQueryResponse(targetTlv, mlIidTlv, NULL, aMessageInfo.GetPeerAddr());
ExitNow();
}
for (ChildTable::Iterator iter(GetInstance(), Child::kInStateValid); !iter.IsDone(); iter++)
{
Child &child = *iter.GetChild();
if (child.IsFullThreadDevice() || child.GetLinkFailures() >= Mle::kFailedChildTransmissions)
{
continue;
}
if (child.HasIp6Address(GetInstance(), targetTlv.GetTarget()))
{
mlIidTlv.SetIid(child.GetExtAddress());
lastTransactionTimeTlv.SetTime(TimerMilli::GetNow() - child.GetLastHeard());
SendAddressQueryResponse(targetTlv, mlIidTlv, &lastTransactionTimeTlv, aMessageInfo.GetPeerAddr());
ExitNow();
}
}
exit:
return;
}
void AddressResolver::SendAddressQueryResponse(const ThreadTargetTlv & aTargetTlv,
const ThreadMeshLocalEidTlv & aMlEidTlv,
const ThreadLastTransactionTimeTlv *aLastTransactionTimeTlv,
const Ip6::Address & aDestination)
{
otError error;
Coap::Message * message;
ThreadRloc16Tlv rloc16Tlv;
Ip6::MessageInfo messageInfo;
VerifyOrExit((message = Get<Coap::Coap>().NewMessage()) != NULL, error = OT_ERROR_NO_BUFS);
message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
SuccessOrExit(error = message->AppendUriPathOptions(OT_URI_PATH_ADDRESS_NOTIFY));
SuccessOrExit(error = message->SetPayloadMarker());
SuccessOrExit(error = aTargetTlv.AppendTo(*message));
SuccessOrExit(error = aMlEidTlv.AppendTo(*message));
rloc16Tlv.Init();
rloc16Tlv.SetRloc16(Get<Mle::MleRouter>().GetRloc16());
SuccessOrExit(error = rloc16Tlv.AppendTo(*message));
if (aLastTransactionTimeTlv != NULL)
{
SuccessOrExit(error = aLastTransactionTimeTlv->AppendTo(*message));
}
messageInfo.SetPeerAddr(aDestination);
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
otLogInfoArp("Sending address notification for target %s", aTargetTlv.GetTarget().ToString().AsCString());
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
}
void AddressResolver::HandleTimer(Timer &aTimer)
{
aTimer.GetOwner<AddressResolver>().HandleTimer();
}
void AddressResolver::HandleTimer(void)
{
bool continueTimer = false;
for (int i = 0; i < kCacheEntries; i++)
{
if (mCache[i].mState != Cache::kStateQuery)
{
continue;
}
continueTimer = true;
if (mCache[i].mTimeout > 0)
{
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);
}
}
else if (mCache[i].mRetryTimeout > 0)
{
mCache[i].mRetryTimeout--;
}
}
if (continueTimer)
{
mTimer.Start(kStateUpdatePeriod);
}
}
void AddressResolver::HandleIcmpReceive(void * aContext,
otMessage * aMessage,
const otMessageInfo *aMessageInfo,
const otIcmp6Header *aIcmpHeader)
{
OT_UNUSED_VARIABLE(aMessageInfo);
static_cast<AddressResolver *>(aContext)->HandleIcmpReceive(*static_cast<Message *>(aMessage),
*static_cast<const Ip6::MessageInfo *>(aMessageInfo),
*static_cast<const Ip6::IcmpHeader *>(aIcmpHeader));
}
void AddressResolver::HandleIcmpReceive(Message & aMessage,
const Ip6::MessageInfo &aMessageInfo,
const Ip6::IcmpHeader & aIcmpHeader)
{
OT_UNUSED_VARIABLE(aMessageInfo);
Ip6::Header ip6Header;
VerifyOrExit(aIcmpHeader.GetType() == Ip6::IcmpHeader::kTypeDstUnreach);
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;
}
}
exit:
return;
}
} // namespace ot
#endif // OPENTHREAD_FTD