blob: 5171aa6642da110cef88909be8f801de4a877ee1 [file] [log] [blame]
/*
* Copyright (c) 2016, 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 the Thread Network Data managed by the Thread Leader.
*/
#include "network_data_leader.hpp"
#include "coap/coap_message.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
#include "common/logging.hpp"
#include "common/message.hpp"
#include "common/random.hpp"
#include "common/timer.hpp"
#include "mac/mac_types.hpp"
#include "thread/lowpan.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_tlvs.hpp"
#include "thread/uri_paths.hpp"
namespace ot {
namespace NetworkData {
RegisterLogModule("NetworkData");
void LeaderBase::Reset(void)
{
mVersion = Random::NonCrypto::GetUint8();
mStableVersion = Random::NonCrypto::GetUint8();
SetLength(0);
SignalNetDataChanged();
}
Error LeaderBase::GetServiceId(uint32_t aEnterpriseNumber,
const ServiceData &aServiceData,
bool aServerStable,
uint8_t &aServiceId) const
{
Error error = kErrorNotFound;
Iterator iterator = kIteratorInit;
ServiceConfig serviceConfig;
ServiceData serviceData;
while (GetNextService(iterator, serviceConfig) == kErrorNone)
{
serviceConfig.GetServiceData(serviceData);
if (aEnterpriseNumber == serviceConfig.mEnterpriseNumber && aServiceData == serviceData &&
aServerStable == serviceConfig.mServerConfig.mStable)
{
aServiceId = serviceConfig.mServiceId;
ExitNow(error = kErrorNone);
}
}
exit:
return error;
}
Error LeaderBase::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const
{
Error error = kErrorNotFound;
Iterator iterator = kIteratorInit;
ExternalRouteConfig config;
while (GetNextExternalRoute(iterator, config) == kErrorNone)
{
if (!config.mNat64 || !config.GetPrefix().IsValidNat64())
{
continue;
}
if ((error == kErrorNotFound) || (config.mPreference > aConfig.mPreference) ||
(config.mPreference == aConfig.mPreference && config.GetPrefix() < aConfig.GetPrefix()))
{
aConfig = config;
error = kErrorNone;
}
}
return error;
}
const PrefixTlv *LeaderBase::FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
{
// This method iterates over Prefix TLVs which match a given IPv6
// `aAddress`. If `aPrevTlv` is `nullptr` we start from the
// beginning. Otherwise, we search for a match after `aPrevTlv`.
// This method returns a pointer to the next matching Prefix TLV
// when found, or `nullptr` if no match is found.
const PrefixTlv *prefixTlv;
TlvIterator tlvIterator((aPrevTlv == nullptr) ? GetTlvsStart() : aPrevTlv->GetNext(), GetTlvsEnd());
while ((prefixTlv = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
{
if (aAddress.MatchesPrefix(prefixTlv->GetPrefix(), prefixTlv->GetPrefixLength()))
{
break;
}
}
return prefixTlv;
}
Error LeaderBase::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext) const
{
const PrefixTlv *prefixTlv = nullptr;
const ContextTlv *contextTlv;
aContext.mPrefix.SetLength(0);
if (Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress))
{
GetContextForMeshLocalPrefix(aContext);
}
while ((prefixTlv = FindNextMatchingPrefixTlv(aAddress, prefixTlv)) != nullptr)
{
contextTlv = prefixTlv->FindSubTlv<ContextTlv>();
if (contextTlv == nullptr)
{
continue;
}
if (prefixTlv->GetPrefixLength() > aContext.mPrefix.GetLength())
{
prefixTlv->CopyPrefixTo(aContext.mPrefix);
aContext.mContextId = contextTlv->GetContextId();
aContext.mCompressFlag = contextTlv->IsCompress();
aContext.mIsValid = true;
}
}
return (aContext.mPrefix.GetLength() > 0) ? kErrorNone : kErrorNotFound;
}
Error LeaderBase::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const
{
Error error = kErrorNotFound;
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
const PrefixTlv *prefixTlv;
if (aContextId == Mle::kMeshLocalPrefixContextId)
{
GetContextForMeshLocalPrefix(aContext);
ExitNow(error = kErrorNone);
}
while ((prefixTlv = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
{
const ContextTlv *contextTlv = prefixTlv->FindSubTlv<ContextTlv>();
if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId))
{
continue;
}
prefixTlv->CopyPrefixTo(aContext.mPrefix);
aContext.mContextId = contextTlv->GetContextId();
aContext.mCompressFlag = contextTlv->IsCompress();
aContext.mIsValid = true;
ExitNow(error = kErrorNone);
}
exit:
return error;
}
void LeaderBase::GetContextForMeshLocalPrefix(Lowpan::Context &aContext) const
{
aContext.mPrefix.Set(Get<Mle::MleRouter>().GetMeshLocalPrefix());
aContext.mContextId = Mle::kMeshLocalPrefixContextId;
aContext.mCompressFlag = true;
aContext.mIsValid = true;
}
bool LeaderBase::IsOnMesh(const Ip6::Address &aAddress) const
{
const PrefixTlv *prefixTlv = nullptr;
bool isOnMesh = false;
VerifyOrExit(!Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress), isOnMesh = true);
while ((prefixTlv = FindNextMatchingPrefixTlv(aAddress, prefixTlv)) != nullptr)
{
TlvIterator subTlvIterator(*prefixTlv);
const BorderRouterTlv *brTlv;
while ((brTlv = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
{
for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry();
entry = entry->GetNext())
{
if (entry->IsOnMesh())
{
ExitNow(isOnMesh = true);
}
}
}
}
exit:
return isOnMesh;
}
Error LeaderBase::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint16_t &aRloc16) const
{
Error error = kErrorNoRoute;
const PrefixTlv *prefixTlv = nullptr;
while ((prefixTlv = FindNextMatchingPrefixTlv(aSource, prefixTlv)) != nullptr)
{
if (prefixTlv->FindSubTlv<BorderRouterTlv>() == nullptr)
{
continue;
}
if (ExternalRouteLookup(prefixTlv->GetDomainId(), aDestination, aRloc16) == kErrorNone)
{
ExitNow(error = kErrorNone);
}
if (DefaultRouteLookup(*prefixTlv, aRloc16) == kErrorNone)
{
ExitNow(error = kErrorNone);
}
}
exit:
return error;
}
template <typename EntryType>
int LeaderBase::CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const
{
// `EntryType` can be `HasRouteEntry` or `BorderRouterEntry`.
return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc());
}
int LeaderBase::CompareRouteEntries(int8_t aFirstPreference,
uint16_t aFirstRloc,
int8_t aSecondPreference,
uint16_t aSecondRloc) const
{
// Performs three-way comparison between two BR entries.
int result;
// Prefer the entry with higher preference.
result = ThreeWayCompare(aFirstPreference, aSecondPreference);
VerifyOrExit(result == 0);
#if OPENTHREAD_MTD
// On MTD, prefer the BR that is this device itself. This handles
// the uncommon case where an MTD itself may be acting as BR.
result = ThreeWayCompare((aFirstRloc == Get<Mle::Mle>().GetRloc16()), (aSecondRloc == Get<Mle::Mle>().GetRloc16()));
#endif
#if OPENTHREAD_FTD
// If all the same, prefer the one with lower mesh path cost.
// Lower cost is preferred so we pass the second entry's cost as
// the first argument in the call to `ThreeWayCompare()`, i.e.,
// if the second entry's cost is larger, we return 1 indicating
// that the first entry is preferred over the second one.
result = ThreeWayCompare(Get<RouterTable>().GetPathCost(aSecondRloc), Get<RouterTable>().GetPathCost(aFirstRloc));
VerifyOrExit(result == 0);
// If all the same, prefer the BR acting as a router over an
// end device.
result = ThreeWayCompare(Mle::IsActiveRouter(aFirstRloc), Mle::IsActiveRouter(aSecondRloc));
#endif
exit:
return result;
}
Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const
{
Error error = kErrorNoRoute;
const PrefixTlv *prefixTlv = nullptr;
const HasRouteEntry *bestRouteEntry = nullptr;
uint8_t bestMatchLength = 0;
while ((prefixTlv = FindNextMatchingPrefixTlv(aDestination, prefixTlv)) != nullptr)
{
const HasRouteTlv *hasRoute;
uint8_t prefixLength = prefixTlv->GetPrefixLength();
TlvIterator subTlvIterator(*prefixTlv);
if (prefixTlv->GetDomainId() != aDomainId)
{
continue;
}
if ((bestRouteEntry != nullptr) && (prefixLength <= bestMatchLength))
{
continue;
}
while ((hasRoute = subTlvIterator.Iterate<HasRouteTlv>()) != nullptr)
{
for (const HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
entry = entry->GetNext())
{
if ((bestRouteEntry == nullptr) || (prefixLength > bestMatchLength) ||
CompareRouteEntries(*entry, *bestRouteEntry) > 0)
{
bestRouteEntry = entry;
bestMatchLength = prefixLength;
}
}
}
}
if (bestRouteEntry != nullptr)
{
aRloc16 = bestRouteEntry->GetRloc();
error = kErrorNone;
}
return error;
}
Error LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const
{
Error error = kErrorNoRoute;
TlvIterator subTlvIterator(aPrefix);
const BorderRouterTlv *brTlv;
const BorderRouterEntry *route = nullptr;
while ((brTlv = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
{
for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry();
entry = entry->GetNext())
{
if (!entry->IsDefaultRoute())
{
continue;
}
if (route == nullptr || CompareRouteEntries(*entry, *route) > 0)
{
route = entry;
}
}
}
if (route != nullptr)
{
aRloc16 = route->GetRloc();
error = kErrorNone;
}
return error;
}
Error LeaderBase::SetNetworkData(uint8_t aVersion,
uint8_t aStableVersion,
Type aType,
const Message &aMessage,
uint16_t aOffset,
uint16_t aLength)
{
Error error = kErrorNone;
VerifyOrExit(aLength <= kMaxSize, error = kErrorParse);
SuccessOrExit(error = aMessage.Read(aOffset, GetBytes(), aLength));
SetLength(static_cast<uint8_t>(aLength));
mVersion = aVersion;
mStableVersion = aStableVersion;
if (aType == kStableSubset)
{
RemoveTemporaryData();
}
#if OPENTHREAD_FTD
if (Get<Mle::MleRouter>().IsLeader())
{
Get<Leader>().HandleNetworkDataRestoredAfterReset();
}
#endif
DumpDebg("SetNetworkData", GetBytes(), GetLength());
SignalNetDataChanged();
exit:
return error;
}
Error LeaderBase::SetCommissioningData(const uint8_t *aValue, uint8_t aValueLength)
{
Error error = kErrorNone;
CommissioningDataTlv *commissioningDataTlv;
RemoveCommissioningData();
if (aValueLength > 0)
{
VerifyOrExit(aValueLength <= kMaxSize - sizeof(CommissioningDataTlv), error = kErrorNoBufs);
commissioningDataTlv = As<CommissioningDataTlv>(AppendTlv(sizeof(CommissioningDataTlv) + aValueLength));
VerifyOrExit(commissioningDataTlv != nullptr, error = kErrorNoBufs);
commissioningDataTlv->Init();
commissioningDataTlv->SetLength(aValueLength);
memcpy(commissioningDataTlv->GetValue(), aValue, aValueLength);
}
mVersion++;
SignalNetDataChanged();
exit:
return error;
}
const CommissioningDataTlv *LeaderBase::GetCommissioningData(void) const
{
return NetworkDataTlv::Find<CommissioningDataTlv>(GetTlvsStart(), GetTlvsEnd());
}
const MeshCoP::Tlv *LeaderBase::GetCommissioningDataSubTlv(MeshCoP::Tlv::Type aType) const
{
const MeshCoP::Tlv *rval = nullptr;
const NetworkDataTlv *commissioningDataTlv;
commissioningDataTlv = GetCommissioningData();
VerifyOrExit(commissioningDataTlv != nullptr);
rval = MeshCoP::Tlv::FindTlv(commissioningDataTlv->GetValue(), commissioningDataTlv->GetLength(), aType);
exit:
return rval;
}
bool LeaderBase::IsJoiningEnabled(void) const
{
const MeshCoP::Tlv *steeringData;
bool rval = false;
VerifyOrExit(GetCommissioningDataSubTlv(MeshCoP::Tlv::kBorderAgentLocator) != nullptr);
steeringData = GetCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData);
VerifyOrExit(steeringData != nullptr);
for (int i = 0; i < steeringData->GetLength(); i++)
{
if (steeringData->GetValue()[i] != 0)
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
void LeaderBase::RemoveCommissioningData(void)
{
CommissioningDataTlv *tlv = GetCommissioningData();
VerifyOrExit(tlv != nullptr);
RemoveTlv(tlv);
exit:
return;
}
Error LeaderBase::SteeringDataCheck(const FilterIndexes &aFilterIndexes) const
{
Error error = kErrorNone;
const MeshCoP::Tlv *steeringDataTlv;
MeshCoP::SteeringData steeringData;
steeringDataTlv = GetCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData);
VerifyOrExit(steeringDataTlv != nullptr, error = kErrorInvalidState);
As<MeshCoP::SteeringDataTlv>(steeringDataTlv)->CopyTo(steeringData);
VerifyOrExit(steeringData.Contains(aFilterIndexes), error = kErrorNotFound);
exit:
return error;
}
Error LeaderBase::SteeringDataCheckJoiner(const Mac::ExtAddress &aEui64) const
{
FilterIndexes filterIndexes;
Mac::ExtAddress joinerId;
MeshCoP::ComputeJoinerId(aEui64, joinerId);
MeshCoP::SteeringData::CalculateHashBitIndexes(joinerId, filterIndexes);
return SteeringDataCheck(filterIndexes);
}
Error LeaderBase::SteeringDataCheckJoiner(const MeshCoP::JoinerDiscerner &aDiscerner) const
{
FilterIndexes filterIndexes;
MeshCoP::SteeringData::CalculateHashBitIndexes(aDiscerner, filterIndexes);
return SteeringDataCheck(filterIndexes);
}
void LeaderBase::SignalNetDataChanged(void)
{
mMaxLength = Max(mMaxLength, GetLength());
Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
}
} // namespace NetworkData
} // namespace ot