| /* |
| * Copyright (c) 2020, 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 Backbone Router management. |
| */ |
| |
| #include "bbr_manager.hpp" |
| |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| |
| #include "common/as_core_type.hpp" |
| #include "common/code_utils.hpp" |
| #include "common/instance.hpp" |
| #include "common/locator_getters.hpp" |
| #include "common/log.hpp" |
| #include "common/random.hpp" |
| #include "thread/mle_types.hpp" |
| #include "thread/thread_netif.hpp" |
| #include "thread/thread_tlvs.hpp" |
| #include "thread/uri_paths.hpp" |
| |
| namespace ot { |
| |
| namespace BackboneRouter { |
| |
| RegisterLogModule("BbrManager"); |
| |
| Manager::Manager(Instance &aInstance) |
| : InstanceLocator(aInstance) |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| , mMulticastListenerRegistration(UriPath::kMlr, Manager::HandleMulticastListenerRegistration, this) |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| , mDuaRegistration(UriPath::kDuaRegistrationRequest, Manager::HandleDuaRegistration, this) |
| , mBackboneQuery(UriPath::kBackboneQuery, Manager::HandleBackboneQuery, this) |
| , mBackboneAnswer(UriPath::kBackboneAnswer, Manager::HandleBackboneAnswer, this) |
| , mNdProxyTable(aInstance) |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| , mMulticastListenersTable(aInstance) |
| #endif |
| , mTimer(aInstance, Manager::HandleTimer) |
| , mBackboneTmfAgent(aInstance) |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| , mDuaResponseStatus(ThreadStatusTlv::kDuaSuccess) |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| , mMlrResponseStatus(ThreadStatusTlv::kMlrSuccess) |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| , mDuaResponseIsSpecified(false) |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| , mMlrResponseIsSpecified(false) |
| #endif |
| #endif |
| { |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| mBackboneTmfAgent.AddResource(mBackboneQuery); |
| mBackboneTmfAgent.AddResource(mBackboneAnswer); |
| #endif |
| } |
| |
| void Manager::HandleNotifierEvents(Events aEvents) |
| { |
| Error error; |
| |
| if (aEvents.Contains(kEventThreadBackboneRouterStateChanged)) |
| { |
| if (Get<Local>().GetState() == OT_BACKBONE_ROUTER_STATE_DISABLED) |
| { |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| Get<Tmf::Agent>().RemoveResource(mMulticastListenerRegistration); |
| mMulticastListenersTable.Clear(); |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| Get<Tmf::Agent>().RemoveResource(mDuaRegistration); |
| #endif |
| mTimer.Stop(); |
| |
| error = mBackboneTmfAgent.Stop(); |
| |
| if (error != kErrorNone) |
| { |
| LogWarn("Stop Backbone TMF agent: %s", ErrorToString(error)); |
| } |
| else |
| { |
| LogInfo("Stop Backbone TMF agent: %s", ErrorToString(error)); |
| } |
| } |
| else |
| { |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| Get<Tmf::Agent>().AddResource(mMulticastListenerRegistration); |
| #endif |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| Get<Tmf::Agent>().AddResource(mDuaRegistration); |
| #endif |
| if (!mTimer.IsRunning()) |
| { |
| mTimer.Start(kTimerInterval); |
| } |
| |
| error = mBackboneTmfAgent.Start(); |
| |
| LogError("Start Backbone TMF agent", error); |
| } |
| } |
| } |
| |
| void Manager::HandleTimer(Timer &aTimer) |
| { |
| aTimer.Get<Manager>().HandleTimer(); |
| } |
| |
| void Manager::HandleTimer(void) |
| { |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| mMulticastListenersTable.Expire(); |
| #endif |
| |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| mNdProxyTable.HandleTimer(); |
| #endif |
| |
| mTimer.Start(kTimerInterval); |
| } |
| |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| Error error = kErrorNone; |
| bool isPrimary = Get<Local>().IsPrimary(); |
| ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess; |
| BackboneRouterConfig config; |
| |
| uint16_t addressesOffset, addressesLength; |
| Ip6::Address address; |
| Ip6::Address addresses[Ip6AddressesTlv::kMaxAddresses]; |
| uint8_t failedAddressNum = 0; |
| uint8_t successAddressNum = 0; |
| TimeMilli expireTime; |
| uint32_t timeout; |
| uint16_t commissionerSessionId; |
| bool hasCommissionerSessionIdTlv = false; |
| bool processTimeoutTlv = false; |
| |
| VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse); |
| |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| // Required by Test Specification 5.10.22 DUA-TC-26, only for certification purpose |
| if (mMlrResponseIsSpecified) |
| { |
| mMlrResponseIsSpecified = false; |
| ExitNow(status = mMlrResponseStatus); |
| } |
| #endif |
| |
| VerifyOrExit(isPrimary, status = ThreadStatusTlv::kMlrBbrNotPrimary); |
| |
| // TODO: (MLR) send configured MLR response for Reference Device |
| |
| if (Tlv::Find<ThreadCommissionerSessionIdTlv>(aMessage, commissionerSessionId) == kErrorNone) |
| { |
| const MeshCoP::CommissionerSessionIdTlv *commissionerSessionIdTlv = As<MeshCoP::CommissionerSessionIdTlv>( |
| Get<NetworkData::Leader>().GetCommissioningDataSubTlv(MeshCoP::Tlv::kCommissionerSessionId)); |
| |
| VerifyOrExit(commissionerSessionIdTlv != nullptr && |
| commissionerSessionIdTlv->GetCommissionerSessionId() == commissionerSessionId, |
| status = ThreadStatusTlv::kMlrGeneralFailure); |
| |
| hasCommissionerSessionIdTlv = true; |
| } |
| |
| processTimeoutTlv = hasCommissionerSessionIdTlv && (Tlv::Find<ThreadTimeoutTlv>(aMessage, timeout) == kErrorNone); |
| |
| VerifyOrExit(Tlv::FindTlvValueOffset(aMessage, Ip6AddressesTlv::kIp6Addresses, addressesOffset, addressesLength) == |
| kErrorNone, |
| error = kErrorParse); |
| VerifyOrExit(addressesLength % sizeof(Ip6::Address) == 0, status = ThreadStatusTlv::kMlrGeneralFailure); |
| VerifyOrExit(addressesLength / sizeof(Ip6::Address) <= Ip6AddressesTlv::kMaxAddresses, |
| status = ThreadStatusTlv::kMlrGeneralFailure); |
| |
| if (!processTimeoutTlv) |
| { |
| IgnoreError(Get<Leader>().GetConfig(config)); |
| |
| timeout = config.mMlrTimeout; |
| } |
| else |
| { |
| VerifyOrExit(timeout < UINT32_MAX, status = ThreadStatusTlv::kMlrNoPersistent); |
| |
| if (timeout != 0) |
| { |
| uint32_t origTimeout = timeout; |
| |
| timeout = OT_MIN(timeout, static_cast<uint32_t>(Mle::kMlrTimeoutMax)); |
| |
| if (timeout != origTimeout) |
| { |
| LogNote("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout); |
| } |
| } |
| } |
| |
| expireTime = TimerMilli::GetNow() + TimeMilli::SecToMsec(timeout); |
| |
| for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address)) |
| { |
| IgnoreError(aMessage.Read(addressesOffset + offset, address)); |
| |
| if (timeout == 0) |
| { |
| mMulticastListenersTable.Remove(address); |
| |
| // Put successfully de-registered addresses at the end of `addresses`. |
| addresses[Ip6AddressesTlv::kMaxAddresses - (++successAddressNum)] = address; |
| } |
| else |
| { |
| bool failed = true; |
| |
| switch (mMulticastListenersTable.Add(address, expireTime)) |
| { |
| case kErrorNone: |
| failed = false; |
| break; |
| case kErrorInvalidArgs: |
| if (status == ThreadStatusTlv::kMlrSuccess) |
| { |
| status = ThreadStatusTlv::kMlrInvalid; |
| } |
| break; |
| case kErrorNoBufs: |
| if (status == ThreadStatusTlv::kMlrSuccess) |
| { |
| status = ThreadStatusTlv::kMlrNoResources; |
| } |
| break; |
| default: |
| OT_ASSERT(false); |
| } |
| |
| if (failed) |
| { |
| addresses[failedAddressNum++] = address; |
| } |
| else |
| { |
| // Put successfully registered addresses at the end of `addresses`. |
| addresses[Ip6AddressesTlv::kMaxAddresses - (++successAddressNum)] = address; |
| } |
| } |
| } |
| |
| exit: |
| if (error == kErrorNone) |
| { |
| SendMulticastListenerRegistrationResponse(aMessage, aMessageInfo, status, addresses, failedAddressNum); |
| } |
| |
| if (successAddressNum > 0) |
| { |
| SendBackboneMulticastListenerRegistration(&addresses[Ip6AddressesTlv::kMaxAddresses - successAddressNum], |
| successAddressNum, timeout); |
| } |
| } |
| |
| void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage, |
| const Ip6::MessageInfo & aMessageInfo, |
| ThreadStatusTlv::MlrStatus aStatus, |
| Ip6::Address * aFailedAddresses, |
| uint8_t aFailedAddressNum) |
| { |
| Error error = kErrorNone; |
| Coap::Message *message; |
| |
| message = Get<Tmf::Agent>().NewResponseMessage(aMessage); |
| VerifyOrExit(message != nullptr, error = kErrorNoBufs); |
| |
| SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus)); |
| |
| if (aFailedAddressNum > 0) |
| { |
| Ip6AddressesTlv addressesTlv; |
| |
| addressesTlv.Init(); |
| addressesTlv.SetLength(sizeof(Ip6::Address) * aFailedAddressNum); |
| SuccessOrExit(error = message->Append(addressesTlv)); |
| |
| for (uint8_t i = 0; i < aFailedAddressNum; i++) |
| { |
| SuccessOrExit(error = message->Append(aFailedAddresses[i])); |
| } |
| } |
| |
| SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo)); |
| |
| exit: |
| FreeMessageOnError(message, error); |
| LogInfo("Sent MLR.rsp (status=%d): %s", aStatus, ErrorToString(error)); |
| } |
| |
| void Manager::SendBackboneMulticastListenerRegistration(const Ip6::Address *aAddresses, |
| uint8_t aAddressNum, |
| uint32_t aTimeout) |
| { |
| Error error = kErrorNone; |
| Coap::Message * message = nullptr; |
| Ip6::MessageInfo messageInfo; |
| Ip6AddressesTlv addressesTlv; |
| BackboneTmfAgent &backboneTmf = Get<BackboneRouter::BackboneTmfAgent>(); |
| |
| OT_ASSERT(aAddressNum >= Ip6AddressesTlv::kMinAddresses && aAddressNum <= Ip6AddressesTlv::kMaxAddresses); |
| |
| message = backboneTmf.NewNonConfirmablePostMessage(UriPath::kBackboneMlr); |
| VerifyOrExit(message != nullptr, error = kErrorNoBufs); |
| |
| addressesTlv.Init(); |
| addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum); |
| SuccessOrExit(error = message->Append(addressesTlv)); |
| SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum)); |
| |
| SuccessOrExit(error = Tlv::Append<ThreadTimeoutTlv>(*message, aTimeout)); |
| |
| messageInfo.SetPeerAddr(Get<Local>().GetAllNetworkBackboneRoutersAddress()); |
| messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); // TODO: Provide API for configuring Backbone COAP port. |
| |
| messageInfo.SetHopLimit(Mle::kDefaultBackboneHoplimit); |
| messageInfo.SetIsHostInterface(true); |
| |
| SuccessOrExit(error = backboneTmf.SendMessage(*message, messageInfo)); |
| |
| exit: |
| FreeMessageOnError(message, error); |
| LogInfo("Sent BMLR.ntf: %s", ErrorToString(error)); |
| } |
| #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| void Manager::HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| Error error = kErrorNone; |
| ThreadStatusTlv::DuaStatus status = ThreadStatusTlv::kDuaSuccess; |
| bool isPrimary = Get<Local>().IsPrimary(); |
| uint32_t lastTransactionTime; |
| bool hasLastTransactionTime; |
| Ip6::Address target; |
| Ip6::InterfaceIdentifier meshLocalIid; |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| Coap::Code duaRespCoapCode = Coap::kCodeEmpty; |
| #endif |
| |
| VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator(), error = kErrorDrop); |
| VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse); |
| |
| SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target)); |
| SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid)); |
| |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| if (mDuaResponseIsSpecified && (mDuaResponseTargetMlIid.IsUnspecified() || mDuaResponseTargetMlIid == meshLocalIid)) |
| { |
| mDuaResponseIsSpecified = false; |
| if (mDuaResponseStatus >= Coap::kCodeResponseMin) |
| { |
| duaRespCoapCode = static_cast<Coap::Code>(mDuaResponseStatus); |
| } |
| else |
| { |
| status = static_cast<ThreadStatusTlv::DuaStatus>(mDuaResponseStatus); |
| } |
| ExitNow(); |
| } |
| #endif |
| |
| VerifyOrExit(isPrimary, status = ThreadStatusTlv::kDuaNotPrimary); |
| VerifyOrExit(Get<Leader>().HasDomainPrefix(), status = ThreadStatusTlv::kDuaGeneralFailure); |
| VerifyOrExit(Get<Leader>().IsDomainUnicast(target), status = ThreadStatusTlv::kDuaInvalid); |
| |
| hasLastTransactionTime = (Tlv::Find<ThreadLastTransactionTimeTlv>(aMessage, lastTransactionTime) == kErrorNone); |
| |
| switch (mNdProxyTable.Register(target.GetIid(), meshLocalIid, aMessageInfo.GetPeerAddr().GetIid().GetLocator(), |
| hasLastTransactionTime ? &lastTransactionTime : nullptr)) |
| { |
| case kErrorNone: |
| // TODO: update its EID-to-RLOC Map Cache based on the pair {DUA, RLOC16-source} which is gleaned from the |
| // DUA.req packet according to Thread Spec. 5.23.3.6.2 |
| break; |
| case kErrorDuplicated: |
| status = ThreadStatusTlv::kDuaDuplicate; |
| break; |
| case kErrorNoBufs: |
| status = ThreadStatusTlv::kDuaNoResources; |
| break; |
| default: |
| status = ThreadStatusTlv::kDuaGeneralFailure; |
| break; |
| } |
| |
| exit: |
| LogInfo("Received DUA.req on %s: %s", (isPrimary ? "PBBR" : "SBBR"), ErrorToString(error)); |
| |
| if (error == kErrorNone) |
| { |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| if (duaRespCoapCode != Coap::kCodeEmpty) |
| { |
| IgnoreError(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo, duaRespCoapCode)); |
| } |
| else |
| #endif |
| { |
| SendDuaRegistrationResponse(aMessage, aMessageInfo, target, status); |
| } |
| } |
| } |
| |
| void Manager::SendDuaRegistrationResponse(const Coap::Message & aMessage, |
| const Ip6::MessageInfo & aMessageInfo, |
| const Ip6::Address & aTarget, |
| ThreadStatusTlv::DuaStatus aStatus) |
| { |
| Error error = kErrorNone; |
| Coap::Message *message; |
| |
| message = Get<Tmf::Agent>().NewResponseMessage(aMessage); |
| VerifyOrExit(message != nullptr, error = kErrorNoBufs); |
| |
| SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus)); |
| SuccessOrExit(Tlv::Append<ThreadTargetTlv>(*message, aTarget)); |
| |
| SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo)); |
| |
| exit: |
| FreeMessageOnError(message, error); |
| LogInfo("Sent DUA.rsp for DUA %s, status %d %s", aTarget.ToString().AsCString(), aStatus, ErrorToString(error)); |
| } |
| #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| void Manager::ConfigNextDuaRegistrationResponse(const Ip6::InterfaceIdentifier *aMlIid, uint8_t aStatus) |
| { |
| mDuaResponseIsSpecified = true; |
| |
| if (aMlIid) |
| { |
| mDuaResponseTargetMlIid = *aMlIid; |
| } |
| else |
| { |
| mDuaResponseTargetMlIid.Clear(); |
| } |
| |
| mDuaResponseStatus = aStatus; |
| } |
| #endif |
| |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE |
| void Manager::ConfigNextMulticastListenerRegistrationResponse(ThreadStatusTlv::MlrStatus aStatus) |
| { |
| mMlrResponseIsSpecified = true; |
| mMlrResponseStatus = aStatus; |
| } |
| #endif |
| #endif |
| |
| #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| NdProxyTable &Manager::GetNdProxyTable(void) |
| { |
| return mNdProxyTable; |
| } |
| |
| bool Manager::ShouldForwardDuaToBackbone(const Ip6::Address &aAddress) |
| { |
| bool forwardToBackbone = false; |
| |
| VerifyOrExit(Get<Local>().IsPrimary()); |
| VerifyOrExit(Get<Leader>().IsDomainUnicast(aAddress)); |
| |
| // Do not forward to Backbone if the DUA is registered on PBBR |
| VerifyOrExit(!mNdProxyTable.IsRegistered(aAddress.GetIid())); |
| // Do not forward to Backbone if the DUA belongs to a MTD Child (which may have failed in DUA registration) |
| VerifyOrExit(Get<NeighborTable>().FindNeighbor(aAddress) == nullptr); |
| // Forward to Backbone only if the DUA is resolved to the PBBR's RLOC16 |
| VerifyOrExit(Get<AddressResolver>().LookUp(aAddress) == Get<Mle::MleRouter>().GetRloc16()); |
| |
| forwardToBackbone = true; |
| |
| exit: |
| return forwardToBackbone; |
| } |
| |
| Error Manager::SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16) |
| { |
| Error error = kErrorNone; |
| Coap::Message * message = nullptr; |
| Ip6::MessageInfo messageInfo; |
| |
| VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState); |
| |
| message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(UriPath::kBackboneQuery); |
| VerifyOrExit(message != nullptr, error = kErrorNoBufs); |
| |
| SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua)); |
| |
| if (aRloc16 != Mac::kShortAddrInvalid) |
| { |
| SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, aRloc16)); |
| } |
| |
| messageInfo.SetPeerAddr(Get<Local>().GetAllDomainBackboneRoutersAddress()); |
| messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); |
| |
| messageInfo.SetHopLimit(Mle::kDefaultBackboneHoplimit); |
| messageInfo.SetIsHostInterface(true); |
| |
| error = mBackboneTmfAgent.SendMessage(*message, messageInfo); |
| |
| exit: |
| LogInfo("SendBackboneQuery for %s (rloc16=%04x): %s", aDua.ToString().AsCString(), aRloc16, ErrorToString(error)); |
| FreeMessageOnError(message, error); |
| return error; |
| } |
| |
| void Manager::HandleBackboneQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Manager *>(aContext)->HandleBackboneQuery(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); |
| } |
| |
| void Manager::HandleBackboneQuery(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| Error error = kErrorNone; |
| Ip6::Address dua; |
| uint16_t rloc16 = Mac::kShortAddrInvalid; |
| NdProxyTable::NdProxy *ndProxy; |
| |
| VerifyOrExit(aMessageInfo.IsHostInterface(), error = kErrorDrop); |
| |
| VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState); |
| VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorParse); |
| |
| SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, dua)); |
| |
| error = Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16); |
| VerifyOrExit(error == kErrorNone || error == kErrorNotFound); |
| |
| LogInfo("Received BB.qry from %s for %s (rloc16=%04x)", aMessageInfo.GetPeerAddr().ToString().AsCString(), |
| dua.ToString().AsCString(), rloc16); |
| |
| ndProxy = mNdProxyTable.ResolveDua(dua); |
| VerifyOrExit(ndProxy != nullptr && !ndProxy->GetDadFlag(), error = kErrorNotFound); |
| |
| error = SendBackboneAnswer(aMessageInfo, dua, rloc16, *ndProxy); |
| |
| exit: |
| LogInfo("HandleBackboneQuery: %s", ErrorToString(error)); |
| } |
| |
| void Manager::HandleBackboneAnswer(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Manager *>(aContext)->HandleBackboneAnswer(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); |
| } |
| |
| void Manager::HandleBackboneAnswer(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| Error error = kErrorNone; |
| bool proactive; |
| Ip6::Address dua; |
| Ip6::InterfaceIdentifier meshLocalIid; |
| uint16_t networkNameOffset, networkNameLength; |
| uint32_t timeSinceLastTransaction; |
| uint16_t srcRloc16 = Mac::kShortAddrInvalid; |
| |
| VerifyOrExit(aMessageInfo.IsHostInterface(), error = kErrorDrop); |
| |
| VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState); |
| VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse); |
| |
| proactive = !aMessage.IsConfirmable(); |
| |
| SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, dua)); |
| SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid)); |
| SuccessOrExit(error = Tlv::Find<ThreadLastTransactionTimeTlv>(aMessage, timeSinceLastTransaction)); |
| |
| SuccessOrExit(error = |
| Tlv::FindTlvValueOffset(aMessage, ThreadTlv::kNetworkName, networkNameOffset, networkNameLength)); |
| |
| error = Tlv::Find<ThreadRloc16Tlv>(aMessage, srcRloc16); |
| VerifyOrExit(error == kErrorNone || error == kErrorNotFound); |
| |
| if (proactive) |
| { |
| HandleProactiveBackboneNotification(dua, meshLocalIid, timeSinceLastTransaction); |
| } |
| else if (srcRloc16 == Mac::kShortAddrInvalid) |
| { |
| HandleDadBackboneAnswer(dua, meshLocalIid); |
| } |
| else |
| { |
| HandleExtendedBackboneAnswer(dua, meshLocalIid, timeSinceLastTransaction, srcRloc16); |
| } |
| |
| SuccessOrExit(error = mBackboneTmfAgent.SendEmptyAck(aMessage, aMessageInfo)); |
| |
| exit: |
| LogInfo("HandleBackboneAnswer: %s", ErrorToString(error)); |
| } |
| |
| Error Manager::SendProactiveBackboneNotification(const Ip6::Address & aDua, |
| const Ip6::InterfaceIdentifier &aMeshLocalIid, |
| uint32_t aTimeSinceLastTransaction) |
| { |
| return SendBackboneAnswer(Get<Local>().GetAllDomainBackboneRoutersAddress(), aDua, aMeshLocalIid, |
| aTimeSinceLastTransaction, Mac::kShortAddrInvalid); |
| } |
| |
| Error Manager::SendBackboneAnswer(const Ip6::MessageInfo & aQueryMessageInfo, |
| const Ip6::Address & aDua, |
| uint16_t aSrcRloc16, |
| const NdProxyTable::NdProxy &aNdProxy) |
| { |
| return SendBackboneAnswer(aQueryMessageInfo.GetPeerAddr(), aDua, aNdProxy.GetMeshLocalIid(), |
| aNdProxy.GetTimeSinceLastTransaction(), aSrcRloc16); |
| } |
| |
| Error Manager::SendBackboneAnswer(const Ip6::Address & aDstAddr, |
| const Ip6::Address & aDua, |
| const Ip6::InterfaceIdentifier &aMeshLocalIid, |
| uint32_t aTimeSinceLastTransaction, |
| uint16_t aSrcRloc16) |
| { |
| Error error = kErrorNone; |
| Coap::Message * message = nullptr; |
| Ip6::MessageInfo messageInfo; |
| bool proactive = aDstAddr.IsMulticast(); |
| |
| VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs); |
| |
| SuccessOrExit(error = message->Init(proactive ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost, |
| UriPath::kBackboneAnswer)); |
| SuccessOrExit(error = message->SetPayloadMarker()); |
| |
| SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua)); |
| |
| SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid)); |
| |
| SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, aTimeSinceLastTransaction)); |
| |
| { |
| const MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData(); |
| |
| SuccessOrExit(error = Tlv::Append<ThreadNetworkNameTlv>(*message, nameData.GetBuffer(), nameData.GetLength())); |
| } |
| |
| if (aSrcRloc16 != Mac::kShortAddrInvalid) |
| { |
| SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, aSrcRloc16)); |
| } |
| |
| messageInfo.SetPeerAddr(aDstAddr); |
| messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); |
| |
| messageInfo.SetHopLimit(Mle::kDefaultBackboneHoplimit); |
| messageInfo.SetIsHostInterface(true); |
| |
| error = mBackboneTmfAgent.SendMessage(*message, messageInfo); |
| |
| exit: |
| LogInfo("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(), |
| aSrcRloc16, ErrorToString(error)); |
| |
| FreeMessageOnError(message, error); |
| return error; |
| } |
| |
| void Manager::HandleDadBackboneAnswer(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid) |
| { |
| Error error = kErrorNone; |
| NdProxyTable::NdProxy *ndProxy = mNdProxyTable.ResolveDua(aDua); |
| bool duplicate = false; |
| |
| OT_UNUSED_VARIABLE(error); |
| |
| VerifyOrExit(ndProxy != nullptr, error = kErrorNotFound); |
| |
| duplicate = ndProxy->GetMeshLocalIid() != aMeshLocalIid; |
| |
| if (duplicate) |
| { |
| Ip6::Address dest; |
| |
| dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), ndProxy->GetRloc16()); |
| Get<AddressResolver>().SendAddressError(aDua, aMeshLocalIid, &dest); |
| } |
| |
| ot::BackboneRouter::NdProxyTable::NotifyDadComplete(*ndProxy, duplicate); |
| |
| exit: |
| LogInfo("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error), |
| aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N"); |
| } |
| |
| void Manager::HandleExtendedBackboneAnswer(const Ip6::Address & aDua, |
| const Ip6::InterfaceIdentifier &aMeshLocalIid, |
| uint32_t aTimeSinceLastTransaction, |
| uint16_t aSrcRloc16) |
| { |
| Ip6::Address dest; |
| |
| dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aSrcRloc16); |
| Get<AddressResolver>().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest); |
| |
| LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lds, rloc16=%04x", aDua.ToString().AsCString(), |
| aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction, aSrcRloc16); |
| } |
| |
| void Manager::HandleProactiveBackboneNotification(const Ip6::Address & aDua, |
| const Ip6::InterfaceIdentifier &aMeshLocalIid, |
| uint32_t aTimeSinceLastTransaction) |
| { |
| Error error = kErrorNone; |
| NdProxyTable::NdProxy *ndProxy = mNdProxyTable.ResolveDua(aDua); |
| |
| OT_UNUSED_VARIABLE(error); |
| |
| VerifyOrExit(ndProxy != nullptr, error = kErrorNotFound); |
| |
| if (ndProxy->GetMeshLocalIid() == aMeshLocalIid) |
| { |
| uint32_t localTimeSinceLastTransaction = ndProxy->GetTimeSinceLastTransaction(); |
| |
| if (aTimeSinceLastTransaction <= localTimeSinceLastTransaction) |
| { |
| BackboneRouter::NdProxyTable::Erase(*ndProxy); |
| } |
| else |
| { |
| IgnoreError(SendProactiveBackboneNotification(aDua, ndProxy->GetMeshLocalIid(), |
| ndProxy->GetTimeSinceLastTransaction())); |
| } |
| } |
| else |
| { |
| // Duplicated address detected, send ADDR_ERR.ntf to ff03::2 in the Thread network |
| BackboneRouter::NdProxyTable::Erase(*ndProxy); |
| Get<AddressResolver>().SendAddressError(aDua, aMeshLocalIid, nullptr); |
| } |
| |
| exit: |
| LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lds", ErrorToString(error), |
| aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction); |
| } |
| #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
| |
| void Manager::LogError(const char *aText, Error aError) const |
| { |
| OT_UNUSED_VARIABLE(aText); |
| |
| if (aError == kErrorNone) |
| { |
| LogInfo("%s: %s", aText, ErrorToString(aError)); |
| } |
| else |
| { |
| LogWarn("%s: %s", aText, ErrorToString(aError)); |
| } |
| } |
| |
| } // namespace BackboneRouter |
| |
| } // namespace ot |
| |
| #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |