/* * 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/locator_getters.hpp" #include "common/log.hpp" #include "common/num_utils.hpp" #include "common/numeric_limits.hpp" #include "common/random.hpp" #include "instance/instance.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_DUA_NDPROXYING_ENABLE , mNdProxyTable(aInstance) #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE , mMulticastListenersTable(aInstance) #endif , mTimer(aInstance) , 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 { } void Manager::HandleNotifierEvents(Events aEvents) { Error error; if (aEvents.Contains(kEventThreadBackboneRouterStateChanged)) { if (!Get().IsEnabled()) { #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE mMulticastListenersTable.Clear(); #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 (!mTimer.IsRunning()) { mTimer.Start(kTimerInterval); } error = mBackboneTmfAgent.Start(); LogError("Start Backbone TMF agent", error); } } } 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 template <> void Manager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { VerifyOrExit(Get().IsEnabled()); HandleMulticastListenerRegistration(aMessage, aMessageInfo); exit: return; } void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; bool isPrimary = Get().IsPrimary(); ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess; Config 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(aMessage, commissionerSessionId) == kErrorNone) { uint16_t localSessionId; VerifyOrExit((Get().FindCommissioningSessionId(localSessionId) == kErrorNone) && (localSessionId == commissionerSessionId), status = ThreadStatusTlv::kMlrGeneralFailure); hasCommissionerSessionIdTlv = true; } processTimeoutTlv = hasCommissionerSessionIdTlv && (Tlv::Find(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().GetConfig(config)); timeout = config.mMlrTimeout; } else { VerifyOrExit(timeout < NumericLimits::kMax, status = ThreadStatusTlv::kMlrNoPersistent); if (timeout != 0) { uint32_t origTimeout = timeout; timeout = Min(timeout, kMaxMlrTimeout); if (timeout != origTimeout) { LogNote("MLR.req: MLR timeout is normalized from %lu to %lu", ToUlong(origTimeout), ToUlong(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().NewResponseMessage(aMessage); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(Tlv::Append(*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().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(); OT_ASSERT(aAddressNum >= Ip6AddressesTlv::kMinAddresses && aAddressNum <= Ip6AddressesTlv::kMaxAddresses); message = backboneTmf.NewNonConfirmablePostMessage(kUriBackboneMlr); 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(*message, aTimeout)); messageInfo.SetPeerAddr(Get().GetAllNetworkBackboneRoutersAddress()); messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); // TODO: Provide API for configuring Backbone COAP port. messageInfo.SetHopLimit(kDefaultHoplimit); 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 template <> void Manager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { VerifyOrExit(Get().IsEnabled()); HandleDuaRegistration(aMessage, aMessageInfo); exit: return; } void Manager::HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; ThreadStatusTlv::DuaStatus status = ThreadStatusTlv::kDuaSuccess; bool isPrimary = Get().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(aMessage, target)); SuccessOrExit(error = Tlv::Find(aMessage, meshLocalIid)); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE if (mDuaResponseIsSpecified && (mDuaResponseTargetMlIid.IsUnspecified() || mDuaResponseTargetMlIid == meshLocalIid)) { mDuaResponseIsSpecified = false; if (mDuaResponseStatus >= Coap::kCodeResponseMin) { duaRespCoapCode = static_cast(mDuaResponseStatus); } else { status = static_cast(mDuaResponseStatus); } ExitNow(); } #endif VerifyOrExit(isPrimary, status = ThreadStatusTlv::kDuaNotPrimary); VerifyOrExit(Get().HasDomainPrefix(), status = ThreadStatusTlv::kDuaGeneralFailure); VerifyOrExit(Get().IsDomainUnicast(target), status = ThreadStatusTlv::kDuaInvalid); hasLastTransactionTime = (Tlv::Find(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().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().NewResponseMessage(aMessage); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(Tlv::Append(*message, aStatus)); SuccessOrExit(Tlv::Append(*message, aTarget)); SuccessOrExit(error = Get().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().IsPrimary()); VerifyOrExit(Get().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().FindNeighbor(aAddress) == nullptr); // Forward to Backbone only if the DUA is resolved to the PBBR's RLOC16 VerifyOrExit(Get().LookUp(aAddress) == Get().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().IsPrimary(), error = kErrorInvalidState); message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(kUriBackboneQuery); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aDua)); if (aRloc16 != Mac::kShortAddrInvalid) { SuccessOrExit(error = Tlv::Append(*message, aRloc16)); } messageInfo.SetPeerAddr(Get().GetAllDomainBackboneRoutersAddress()); messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); messageInfo.SetHopLimit(kDefaultHoplimit); 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; } template <> void Manager::HandleTmf(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().IsPrimary(), error = kErrorInvalidState); VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorParse); SuccessOrExit(error = Tlv::Find(aMessage, dua)); error = Tlv::Find(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)); } template <> void Manager::HandleTmf(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().IsPrimary(), error = kErrorInvalidState); VerifyOrExit(aMessage.IsPostRequest(), error = kErrorParse); proactive = !aMessage.IsConfirmable(); SuccessOrExit(error = Tlv::Find(aMessage, dua)); SuccessOrExit(error = Tlv::Find(aMessage, meshLocalIid)); SuccessOrExit(error = Tlv::Find(aMessage, timeSinceLastTransaction)); SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, ThreadTlv::kNetworkName, networkNameOffset, networkNameLength)); error = Tlv::Find(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().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, kUriBackboneAnswer)); SuccessOrExit(error = message->SetPayloadMarker()); SuccessOrExit(error = Tlv::Append(*message, aDua)); SuccessOrExit(error = Tlv::Append(*message, aMeshLocalIid)); SuccessOrExit(error = Tlv::Append(*message, aTimeSinceLastTransaction)); SuccessOrExit(error = Tlv::Append( *message, Get().GetNetworkName().GetAsCString())); if (aSrcRloc16 != Mac::kShortAddrInvalid) { SuccessOrExit(Tlv::Append(*message, aSrcRloc16)); } messageInfo.SetPeerAddr(aDstAddr); messageInfo.SetPeerPort(BackboneRouter::kBackboneUdpPort); messageInfo.SetHopLimit(kDefaultHoplimit); 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().GetMeshLocalPrefix(), ndProxy->GetRloc16()); Get().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().GetMeshLocalPrefix(), aSrcRloc16); Get().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest); LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lus, rloc16=%04x", aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), ToUlong(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().SendAddressError(aDua, aMeshLocalIid, nullptr); } exit: LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lus", ErrorToString(error), aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), ToUlong(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