blob: 405d12e7fbdbe366abfb6efaf6c74f2024d683d6 [file] [log] [blame]
/*
* 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/logging.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 {
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)
{
otLogWarnBbr("Stop Backbone TMF agent: %s", ErrorToString(error));
}
else
{
otLogInfoBbr("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[kIp6AddressesNumMax];
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) <= kIp6AddressesNumMax,
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)
{
otLogNoteBbr("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[kIp6AddressesNumMax - (++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[kIp6AddressesNumMax - (++successAddressNum)] = address;
}
}
}
exit:
if (error == kErrorNone)
{
SendMulticastListenerRegistrationResponse(aMessage, aMessageInfo, status, addresses, failedAddressNum);
}
if (successAddressNum > 0)
{
SendBackboneMulticastListenerRegistration(&addresses[kIp6AddressesNumMax - 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 = nullptr;
VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
SuccessOrExit(message->SetPayloadMarker());
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);
otLogInfoBbr("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 >= kIp6AddressesNumMin && aAddressNum <= kIp6AddressesNumMax);
VerifyOrExit((message = backboneTmf.NewMessage()) != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kBackboneMlr));
SuccessOrExit(error = message->SetPayloadMarker());
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);
otLogInfoBbr("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:
otLogInfoBbr("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 = nullptr;
VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
SuccessOrExit(message->SetPayloadMarker());
SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
SuccessOrExit(Tlv::Append<ThreadTargetTlv>(*message, aTarget));
SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
exit:
FreeMessageOnError(message, error);
otLogInfoBbr("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);
VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kBackboneQuery));
SuccessOrExit(error = message->SetPayloadMarker());
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:
otLogInfoBbr("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);
otLogInfoBbr("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:
otLogInfoBbr("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:
otLogInfoBbr("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 Mac::NameData nameData = Get<Mac::Mac>().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:
otLogInfoBbr("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:
otLogInfoBbr("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);
otLogInfoBbr("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:
otLogInfoBbr("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)
{
otLogInfoBbr("%s: %s", aText, ErrorToString(aError));
}
else
{
otLogWarnBbr("%s: %s", aText, ErrorToString(aError));
}
}
} // namespace BackboneRouter
} // namespace ot
#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE