/* * 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 Thread NdProxy Table management. */ #include "ndproxy_table.hpp" #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE #include "common/array.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" namespace ot { namespace BackboneRouter { RegisterLogModule("BbrNdProxy"); void NdProxyTable::NdProxy::Init(const Ip6::InterfaceIdentifier &aAddressIid, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint16_t aRloc16, uint32_t aTimeSinceLastTransaction) { OT_ASSERT(!mValid); Clear(); mValid = true; mAddressIid = aAddressIid; mMeshLocalIid = aMeshLocalIid; mDadFlag = true; Update(aRloc16, aTimeSinceLastTransaction); } void NdProxyTable::NdProxy::Update(uint16_t aRloc16, uint32_t aTimeSinceLastTransaction) { OT_ASSERT(mValid); mRloc16 = aRloc16; aTimeSinceLastTransaction = OT_MIN(aTimeSinceLastTransaction, static_cast(Mle::kTimeSinceLastTransactionMax)); mLastRegistrationTime = TimerMilli::GetNow() - TimeMilli::SecToMsec(aTimeSinceLastTransaction); } bool NdProxyTable::MatchesFilter(const NdProxy &aProxy, Filter aFilter) { bool rval = false; switch (aFilter) { case kFilterInvalid: rval = !aProxy.mValid; break; case kFilterValid: rval = aProxy.mValid; break; case kFilterDadInProcess: rval = aProxy.mValid && aProxy.mDadFlag; break; } return rval; } NdProxyTable::Iterator::Iterator(Instance &aInstance, Filter aFilter) : InstanceLocator(aInstance) , mFilter(aFilter) { NdProxyTable &table = GetInstance().Get(); mItem = &table.mProxies[0]; if (!MatchesFilter(*mItem, mFilter)) { Advance(); } } NdProxyTable::Iterator::Iterator(Instance &aInstance, NdProxyTable::Iterator::IteratorType) : InstanceLocator(aInstance) { NdProxyTable &table = GetInstance().Get(); mItem = GetArrayEnd(table.mProxies); } void NdProxyTable::Iterator::Advance(void) { NdProxyTable &table = GetInstance().Get(); do { mItem++; } while (mItem < GetArrayEnd(table.mProxies) && !MatchesFilter(*mItem, mFilter)); } void NdProxyTable::Erase(NdProxy &aNdProxy) { aNdProxy.mValid = false; } void NdProxyTable::HandleDomainPrefixUpdate(Leader::DomainPrefixState aState) { if (aState == Leader::kDomainPrefixAdded || aState == Leader::kDomainPrefixRemoved || aState == Leader::kDomainPrefixRefreshed) { Clear(); } } void NdProxyTable::Clear(void) { for (NdProxy &proxy : mProxies) { proxy.Clear(); } if (mCallback != nullptr) { mCallback(mCallbackContext, OT_BACKBONE_ROUTER_NDPROXY_CLEARED, nullptr); } LogInfo("NdProxyTable::Clear!"); } Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint16_t aRloc16, const uint32_t * aTimeSinceLastTransaction) { Error error = kErrorNone; NdProxy *proxy = FindByAddressIid(aAddressIid); uint32_t timeSinceLastTransaction = aTimeSinceLastTransaction == nullptr ? 0 : *aTimeSinceLastTransaction; if (proxy != nullptr) { VerifyOrExit(proxy->mMeshLocalIid == aMeshLocalIid, error = kErrorDuplicated); proxy->Update(aRloc16, timeSinceLastTransaction); NotifyDuaRegistrationOnBackboneLink(*proxy, /* aIsRenew */ true); ExitNow(); } proxy = FindByMeshLocalIid(aMeshLocalIid); if (proxy != nullptr) { TriggerCallback(OT_BACKBONE_ROUTER_NDPROXY_REMOVED, proxy->mAddressIid); Erase(*proxy); } else { proxy = FindInvalid(); // TODO: evict stale DUA entries to have room for this new DUA. VerifyOrExit(proxy != nullptr, error = kErrorNoBufs); } proxy->Init(aAddressIid, aMeshLocalIid, aRloc16, timeSinceLastTransaction); mIsAnyDadInProcess = true; exit: LogInfo("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error)); return error; } NdProxyTable::NdProxy *NdProxyTable::FindByAddressIid(const Ip6::InterfaceIdentifier &aAddressIid) { NdProxy *found = nullptr; for (NdProxy &proxy : Iterate(kFilterValid)) { if (proxy.mAddressIid == aAddressIid) { ExitNow(found = &proxy); } } exit: LogDebg("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(), found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND"); return found; } NdProxyTable::NdProxy *NdProxyTable::FindByMeshLocalIid(const Ip6::InterfaceIdentifier &aMeshLocalIid) { NdProxy *found = nullptr; for (NdProxy &proxy : Iterate(kFilterValid)) { if (proxy.mMeshLocalIid == aMeshLocalIid) { ExitNow(found = &proxy); } } exit: LogDebg("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(), found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND"); return found; } NdProxyTable::NdProxy *NdProxyTable::FindInvalid(void) { NdProxy *found = nullptr; for (NdProxy &proxy : Iterate(kFilterInvalid)) { ExitNow(found = &proxy); } exit: LogDebg("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND"); return found; } void NdProxyTable::HandleTimer(void) { VerifyOrExit(mIsAnyDadInProcess); mIsAnyDadInProcess = false; for (NdProxy &proxy : Iterate(kFilterDadInProcess)) { if (proxy.IsDadAttemptsComplete()) { proxy.mDadFlag = false; NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false); } else { mIsAnyDadInProcess = true; if (Get().SendBackboneQuery(GetDua(proxy)) == kErrorNone) { proxy.IncreaseDadAttempts(); } } } exit: return; } void NdProxyTable::SetCallback(otBackboneRouterNdProxyCallback aCallback, void *aContext) { mCallback = aCallback; mCallbackContext = aContext; } void NdProxyTable::TriggerCallback(otBackboneRouterNdProxyEvent aEvent, const Ip6::InterfaceIdentifier &aAddressIid) const { Ip6::Address dua; const Ip6::Prefix *prefix = Get().GetDomainPrefix(); VerifyOrExit(mCallback != nullptr); OT_ASSERT(prefix != nullptr); dua.SetPrefix(*prefix); dua.SetIid(aAddressIid); mCallback(mCallbackContext, aEvent, &dua); exit: return; } void NdProxyTable::NotifyDadComplete(NdProxyTable::NdProxy &aNdProxy, bool aDuplicated) { if (aDuplicated) { Erase(aNdProxy); } else { aNdProxy.mDadAttempts = Mle::kDuaDadRepeats; } } Ip6::Address NdProxyTable::GetDua(NdProxy &aNdProxy) { Ip6::Address dua; const Ip6::Prefix *domainPrefix = Get().GetDomainPrefix(); OT_ASSERT(domainPrefix != nullptr); dua.SetPrefix(*domainPrefix); dua.SetIid(aNdProxy.mAddressIid); return dua; } NdProxyTable::NdProxy *NdProxyTable::ResolveDua(const Ip6::Address &aDua) { return Get().IsDomainUnicast(aDua) ? FindByAddressIid(aDua.GetIid()) : nullptr; } void NdProxyTable::NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy &aNdProxy, bool aIsRenew) { if (!aNdProxy.mDadFlag) { TriggerCallback(aIsRenew ? OT_BACKBONE_ROUTER_NDPROXY_RENEWED : OT_BACKBONE_ROUTER_NDPROXY_ADDED, aNdProxy.mAddressIid); IgnoreError(Get().SendProactiveBackboneNotification( GetDua(aNdProxy), aNdProxy.GetMeshLocalIid(), aNdProxy.GetTimeSinceLastTransaction())); } } Error NdProxyTable::GetInfo(const Ip6::Address &aDua, otBackboneRouterNdProxyInfo &aNdProxyInfo) { Error error = kErrorNotFound; VerifyOrExit(Get().IsDomainUnicast(aDua), error = kErrorInvalidArgs); for (NdProxy &proxy : Iterate(kFilterValid)) { if (proxy.mAddressIid == aDua.GetIid()) { aNdProxyInfo.mMeshLocalIid = &proxy.mMeshLocalIid; aNdProxyInfo.mTimeSinceLastTransaction = proxy.GetTimeSinceLastTransaction(); aNdProxyInfo.mRloc16 = proxy.mRloc16; ExitNow(error = kErrorNone); } } exit: return error; } } // namespace BackboneRouter } // namespace ot #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE