| /* |
| * 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 |