| /* |
| * 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<uint32_t>(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<BackboneRouter::NdProxyTable>(); |
| |
| mItem = &table.mProxies[0]; |
| |
| if (!MatchesFilter(*mItem, mFilter)) |
| { |
| Advance(); |
| } |
| } |
| |
| NdProxyTable::Iterator::Iterator(Instance &aInstance, NdProxyTable::Iterator::IteratorType) |
| : InstanceLocator(aInstance) |
| { |
| NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>(); |
| mItem = GetArrayEnd(table.mProxies); |
| } |
| |
| void NdProxyTable::Iterator::Advance(void) |
| { |
| NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>(); |
| |
| 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.IsDadAttamptsComplete()) |
| { |
| proxy.mDadFlag = false; |
| NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false); |
| } |
| else |
| { |
| mIsAnyDadInProcess = true; |
| |
| if (Get<BackboneRouter::Manager>().SendBackboneQuery(GetDua(proxy)) == kErrorNone) |
| { |
| proxy.IncreaseDadAttampts(); |
| } |
| } |
| } |
| |
| 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<BackboneRouter::Leader>().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<BackboneRouter::Leader>().GetDomainPrefix(); |
| |
| OT_ASSERT(domainPrefix != nullptr); |
| |
| dua.SetPrefix(*domainPrefix); |
| dua.SetIid(aNdProxy.mAddressIid); |
| |
| return dua; |
| } |
| |
| NdProxyTable::NdProxy *NdProxyTable::ResolveDua(const Ip6::Address &aDua) |
| { |
| return Get<Leader>().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<BackboneRouter::Manager>().SendProactiveBackboneNotification( |
| GetDua(aNdProxy), aNdProxy.GetMeshLocalIid(), aNdProxy.GetTimeSinceLastTransaction())); |
| } |
| } |
| |
| Error NdProxyTable::GetInfo(const Ip6::Address &aDua, otBackboneRouterNdProxyInfo &aNdProxyInfo) |
| { |
| Error error = kErrorNotFound; |
| |
| VerifyOrExit(Get<Leader>().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 |