| /* |
| * Copyright (c) 2019, 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 local Backbone Router service. |
| */ |
| |
| #include "bbr_local.hpp" |
| |
| #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
| |
| #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" |
| |
| namespace ot { |
| |
| namespace BackboneRouter { |
| |
| RegisterLogModule("BbrLocal"); |
| |
| Local::Local(Instance &aInstance) |
| : InstanceLocator(aInstance) |
| , mState(OT_BACKBONE_ROUTER_STATE_DISABLED) |
| , mMlrTimeout(Mle::kMlrTimeoutDefault) |
| , mReregistrationDelay(Mle::kRegistrationDelayDefault) |
| , mSequenceNumber(Random::NonCrypto::GetUint8() % 127) |
| , mRegistrationJitter(Mle::kBackboneRouterRegistrationJitter) |
| , mIsServiceAdded(false) |
| , mDomainPrefixCallback(nullptr) |
| , mDomainPrefixCallbackContext(nullptr) |
| { |
| mDomainPrefixConfig.GetPrefix().SetLength(0); |
| |
| // Primary Backbone Router Aloc |
| mBackboneRouterPrimaryAloc.InitAsThreadOriginRealmLocalScope(); |
| mBackboneRouterPrimaryAloc.GetAddress().GetIid().SetToLocator(Mle::kAloc16BackboneRouterPrimary); |
| |
| // All Network Backbone Routers Multicast Address. |
| mAllNetworkBackboneRouters.Clear(); |
| |
| mAllNetworkBackboneRouters.mFields.m8[0] = 0xff; // Multicast |
| mAllNetworkBackboneRouters.mFields.m8[1] = 0x32; // Flags = 3, Scope = 2 |
| mAllNetworkBackboneRouters.mFields.m8[15] = 3; // Group ID = 3 |
| |
| // All Domain Backbone Routers Multicast Address. |
| mAllDomainBackboneRouters.Clear(); |
| |
| mAllDomainBackboneRouters.mFields.m8[0] = 0xff; // Multicast |
| mAllDomainBackboneRouters.mFields.m8[1] = 0x32; // Flags = 3, Scope = 2 |
| mAllDomainBackboneRouters.mFields.m8[15] = 3; // Group ID = 3 |
| } |
| |
| void Local::SetEnabled(bool aEnable) |
| { |
| VerifyOrExit(aEnable == (mState == OT_BACKBONE_ROUTER_STATE_DISABLED)); |
| |
| if (aEnable) |
| { |
| SetState(OT_BACKBONE_ROUTER_STATE_SECONDARY); |
| AddDomainPrefixToNetworkData(); |
| IgnoreError(AddService()); |
| } |
| else |
| { |
| RemoveDomainPrefixFromNetworkData(); |
| RemoveService(); |
| SetState(OT_BACKBONE_ROUTER_STATE_DISABLED); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Local::Reset(void) |
| { |
| VerifyOrExit(mState != OT_BACKBONE_ROUTER_STATE_DISABLED); |
| |
| RemoveService(); |
| |
| if (mState == OT_BACKBONE_ROUTER_STATE_PRIMARY) |
| { |
| // Increase sequence number when changing from Primary to Secondary. |
| SequenceNumberIncrease(); |
| Get<Notifier>().Signal(kEventThreadBackboneRouterLocalChanged); |
| SetState(OT_BACKBONE_ROUTER_STATE_SECONDARY); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Local::GetConfig(BackboneRouterConfig &aConfig) const |
| { |
| aConfig.mSequenceNumber = mSequenceNumber; |
| aConfig.mReregistrationDelay = mReregistrationDelay; |
| aConfig.mMlrTimeout = mMlrTimeout; |
| } |
| |
| Error Local::SetConfig(const BackboneRouterConfig &aConfig) |
| { |
| Error error = kErrorNone; |
| bool update = false; |
| |
| #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
| VerifyOrExit(aConfig.mMlrTimeout >= Mle::kMlrTimeoutMin && aConfig.mMlrTimeout <= Mle::kMlrTimeoutMax, |
| error = kErrorInvalidArgs); |
| #endif |
| // Validate configuration according to Thread 1.2.1 Specification 5.21.3.3: |
| // "The Reregistration Delay in seconds MUST be lower than (0.5 * MLR Timeout). It MUST be at least 1." |
| VerifyOrExit(aConfig.mReregistrationDelay >= 1, error = kErrorInvalidArgs); |
| static_assert(sizeof(aConfig.mReregistrationDelay) < sizeof(aConfig.mMlrTimeout), |
| "the calculation below might overflow"); |
| VerifyOrExit(aConfig.mReregistrationDelay * 2 < aConfig.mMlrTimeout, error = kErrorInvalidArgs); |
| |
| if (aConfig.mReregistrationDelay != mReregistrationDelay) |
| { |
| mReregistrationDelay = aConfig.mReregistrationDelay; |
| update = true; |
| } |
| |
| if (aConfig.mMlrTimeout != mMlrTimeout) |
| { |
| mMlrTimeout = aConfig.mMlrTimeout; |
| update = true; |
| } |
| |
| if (aConfig.mSequenceNumber != mSequenceNumber) |
| { |
| mSequenceNumber = aConfig.mSequenceNumber; |
| update = true; |
| } |
| |
| if (update) |
| { |
| Get<Notifier>().Signal(kEventThreadBackboneRouterLocalChanged); |
| |
| IgnoreError(AddService()); |
| } |
| |
| exit: |
| LogBackboneRouterService("Set", error); |
| return error; |
| } |
| |
| Error Local::AddService(bool aForce) |
| { |
| Error error = kErrorInvalidState; |
| NetworkData::Service::BackboneRouter::ServerData serverData; |
| |
| VerifyOrExit(mState != OT_BACKBONE_ROUTER_STATE_DISABLED && Get<Mle::Mle>().IsAttached()); |
| |
| VerifyOrExit(aForce /* if register by force */ || |
| !Get<BackboneRouter::Leader>().HasPrimary() /* if no available Backbone Router service */ || |
| Get<BackboneRouter::Leader>().GetServer16() == Get<Mle::MleRouter>().GetRloc16() |
| /* If the device itself should be BBR. */ |
| ); |
| |
| serverData.SetSequenceNumber(mSequenceNumber); |
| serverData.SetReregistrationDelay(mReregistrationDelay); |
| serverData.SetMlrTimeout(mMlrTimeout); |
| |
| SuccessOrExit(error = Get<NetworkData::Service::Manager>().Add<NetworkData::Service::BackboneRouter>(serverData)); |
| Get<NetworkData::Notifier>().HandleServerDataUpdated(); |
| |
| mIsServiceAdded = true; |
| |
| exit: |
| LogBackboneRouterService("Add", error); |
| return error; |
| } |
| |
| void Local::RemoveService(void) |
| { |
| Error error; |
| |
| SuccessOrExit(error = Get<NetworkData::Service::Manager>().Remove<NetworkData::Service::BackboneRouter>()); |
| Get<NetworkData::Notifier>().HandleServerDataUpdated(); |
| mIsServiceAdded = false; |
| |
| exit: |
| LogBackboneRouterService("Remove", error); |
| } |
| |
| void Local::SetState(BackboneRouterState aState) |
| { |
| VerifyOrExit(mState != aState); |
| |
| if (mState == OT_BACKBONE_ROUTER_STATE_DISABLED) |
| { |
| // Update All Network Backbone Routers Multicast Address for both Secondary and Primary state. |
| mAllNetworkBackboneRouters.SetMulticastNetworkPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix()); |
| } |
| |
| if (mState == OT_BACKBONE_ROUTER_STATE_PRIMARY) |
| { |
| Get<ThreadNetif>().RemoveUnicastAddress(mBackboneRouterPrimaryAloc); |
| } |
| else if (aState == OT_BACKBONE_ROUTER_STATE_PRIMARY) |
| { |
| // Add Primary Backbone Router Aloc for Primary Backbone Router. |
| mBackboneRouterPrimaryAloc.GetAddress().SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix()); |
| Get<ThreadNetif>().AddUnicastAddress(mBackboneRouterPrimaryAloc); |
| } |
| |
| mState = aState; |
| |
| Get<Notifier>().Signal(kEventThreadBackboneRouterStateChanged); |
| |
| exit: |
| return; |
| } |
| |
| void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const BackboneRouterConfig &aConfig) |
| { |
| OT_UNUSED_VARIABLE(aState); |
| |
| VerifyOrExit(mState != OT_BACKBONE_ROUTER_STATE_DISABLED && Get<Mle::MleRouter>().IsAttached()); |
| |
| // Wait some jitter before trying to Register. |
| if (aConfig.mServer16 == Mac::kShortAddrInvalid) |
| { |
| uint8_t delay = 1; |
| |
| if (!Get<Mle::MleRouter>().IsLeader()) |
| { |
| delay += Random::NonCrypto::GetUint8InRange(0, mRegistrationJitter < 255 ? mRegistrationJitter + 1 |
| : mRegistrationJitter); |
| } |
| |
| // Here uses the timer resource in Mle. |
| Get<Mle::MleRouter>().SetBackboneRouterRegistrationDelay(delay); |
| } |
| else if (aConfig.mServer16 != Get<Mle::MleRouter>().GetRloc16()) |
| { |
| Reset(); |
| } |
| else if (!mIsServiceAdded) |
| { |
| // Here original PBBR restores its Backbone Router Service from Thread Network, |
| // Intentionally skips the state update as PBBR will refresh its service. |
| mSequenceNumber = aConfig.mSequenceNumber; |
| mReregistrationDelay = aConfig.mReregistrationDelay; |
| mMlrTimeout = aConfig.mMlrTimeout; |
| SequenceNumberIncrease(); |
| Get<Notifier>().Signal(kEventThreadBackboneRouterLocalChanged); |
| if (AddService(true /* Force registration to refresh and restore Primary state */) == kErrorNone) |
| { |
| Get<NetworkData::Notifier>().HandleServerDataUpdated(); |
| } |
| } |
| else |
| { |
| SetState(OT_BACKBONE_ROUTER_STATE_PRIMARY); |
| } |
| |
| exit: |
| return; |
| } |
| |
| Error Local::GetDomainPrefix(NetworkData::OnMeshPrefixConfig &aConfig) |
| { |
| Error error = kErrorNone; |
| |
| VerifyOrExit(mDomainPrefixConfig.GetPrefix().GetLength() > 0, error = kErrorNotFound); |
| |
| aConfig = mDomainPrefixConfig; |
| |
| exit: |
| return error; |
| } |
| |
| Error Local::RemoveDomainPrefix(const Ip6::Prefix &aPrefix) |
| { |
| Error error = kErrorNone; |
| |
| VerifyOrExit(aPrefix.GetLength() > 0, error = kErrorInvalidArgs); |
| VerifyOrExit(mDomainPrefixConfig.GetPrefix() == aPrefix, error = kErrorNotFound); |
| |
| if (IsEnabled()) |
| { |
| RemoveDomainPrefixFromNetworkData(); |
| } |
| |
| mDomainPrefixConfig.GetPrefix().SetLength(0); |
| |
| exit: |
| return error; |
| } |
| |
| Error Local::SetDomainPrefix(const NetworkData::OnMeshPrefixConfig &aConfig) |
| { |
| Error error = kErrorNone; |
| |
| VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs); |
| |
| if (IsEnabled()) |
| { |
| RemoveDomainPrefixFromNetworkData(); |
| } |
| |
| mDomainPrefixConfig = aConfig; |
| LogDomainPrefix("Set", kErrorNone); |
| |
| if (IsEnabled()) |
| { |
| AddDomainPrefixToNetworkData(); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void Local::ApplyMeshLocalPrefix(void) |
| { |
| VerifyOrExit(IsEnabled()); |
| |
| Get<BackboneTmfAgent>().UnsubscribeMulticast(mAllNetworkBackboneRouters); |
| mAllNetworkBackboneRouters.SetMulticastNetworkPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix()); |
| Get<BackboneTmfAgent>().SubscribeMulticast(mAllNetworkBackboneRouters); |
| |
| if (IsPrimary()) |
| { |
| Get<ThreadNetif>().RemoveUnicastAddress(mBackboneRouterPrimaryAloc); |
| mBackboneRouterPrimaryAloc.GetAddress().SetPrefix(Get<Mle::MleRouter>().GetMeshLocalPrefix()); |
| Get<ThreadNetif>().AddUnicastAddress(mBackboneRouterPrimaryAloc); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Local::HandleDomainPrefixUpdate(Leader::DomainPrefixState aState) |
| { |
| if (!IsEnabled()) |
| { |
| ExitNow(); |
| } |
| |
| if (aState == Leader::kDomainPrefixRemoved || aState == Leader::kDomainPrefixRefreshed) |
| { |
| Get<BackboneTmfAgent>().UnsubscribeMulticast(mAllDomainBackboneRouters); |
| } |
| |
| if (aState == Leader::kDomainPrefixAdded || aState == Leader::kDomainPrefixRefreshed) |
| { |
| mAllDomainBackboneRouters.SetMulticastNetworkPrefix(*Get<Leader>().GetDomainPrefix()); |
| Get<BackboneTmfAgent>().SubscribeMulticast(mAllDomainBackboneRouters); |
| } |
| |
| if (mDomainPrefixCallback != nullptr) |
| { |
| switch (aState) |
| { |
| case Leader::kDomainPrefixAdded: |
| mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED, |
| Get<Leader>().GetDomainPrefix()); |
| break; |
| case Leader::kDomainPrefixRemoved: |
| mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED, |
| Get<Leader>().GetDomainPrefix()); |
| break; |
| case Leader::kDomainPrefixRefreshed: |
| mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED, |
| Get<Leader>().GetDomainPrefix()); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| void Local::RemoveDomainPrefixFromNetworkData(void) |
| { |
| Error error = kErrorNotFound; // only used for logging. |
| |
| if (mDomainPrefixConfig.mPrefix.mLength > 0) |
| { |
| error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mDomainPrefixConfig.GetPrefix()); |
| } |
| |
| LogDomainPrefix("Remove", error); |
| } |
| |
| void Local::SequenceNumberIncrease(void) |
| { |
| switch (mSequenceNumber) |
| { |
| case 126: |
| case 127: |
| mSequenceNumber = 0; |
| break; |
| case 254: |
| case 255: |
| mSequenceNumber = 128; |
| break; |
| default: |
| mSequenceNumber++; |
| break; |
| } |
| } |
| |
| void Local::AddDomainPrefixToNetworkData(void) |
| { |
| Error error = kErrorNotFound; // only used for logging. |
| |
| if (mDomainPrefixConfig.GetPrefix().GetLength() > 0) |
| { |
| error = Get<NetworkData::Local>().AddOnMeshPrefix(mDomainPrefixConfig); |
| } |
| |
| LogDomainPrefix("Add", error); |
| } |
| |
| #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) |
| void Local::LogDomainPrefix(const char *aAction, Error aError) |
| { |
| LogInfo("%s Domain Prefix: %s, %s", aAction, mDomainPrefixConfig.GetPrefix().ToString().AsCString(), |
| ErrorToString(aError)); |
| } |
| |
| void Local::LogBackboneRouterService(const char *aAction, Error aError) |
| { |
| LogInfo("%s BBR Service: seqno (%d), delay (%ds), timeout (%ds), %s", aAction, mSequenceNumber, |
| mReregistrationDelay, mMlrTimeout, ErrorToString(aError)); |
| } |
| #endif |
| |
| void Local::SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext) |
| { |
| mDomainPrefixCallback = aCallback; |
| mDomainPrefixCallbackContext = aContext; |
| } |
| |
| } // namespace BackboneRouter |
| |
| } // namespace ot |
| |
| #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |