blob: 8c28842012b9406b5e9278cc341b0a9a4669aa9e [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);
Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
}
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))
{
aConfig = config;
error = kErrorNone;
}
}
return error;
}
const PrefixTlv *LeaderBase::FindNextMatchingPrefix(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
{
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 * prefix = nullptr;
const ContextTlv *contextTlv;
aContext.mPrefix.SetLength(0);
if (Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress))
{
aContext.mPrefix.Set(Get<Mle::MleRouter>().GetMeshLocalPrefix());
aContext.mContextId = Mle::kMeshLocalPrefixContextId;
aContext.mCompressFlag = true;
}
while ((prefix = FindNextMatchingPrefix(aAddress, prefix)) != nullptr)
{
contextTlv = prefix->FindSubTlv<ContextTlv>();
if (contextTlv == nullptr)
{
continue;
}
if (prefix->GetPrefixLength() > aContext.mPrefix.GetLength())
{
aContext.mPrefix.Set(prefix->GetPrefix(), prefix->GetPrefixLength());
aContext.mContextId = contextTlv->GetContextId();
aContext.mCompressFlag = contextTlv->IsCompress();
}
}
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 *prefix;
if (aContextId == Mle::kMeshLocalPrefixContextId)
{
aContext.mPrefix.Set(Get<Mle::MleRouter>().GetMeshLocalPrefix());
aContext.mContextId = Mle::kMeshLocalPrefixContextId;
aContext.mCompressFlag = true;
ExitNow(error = kErrorNone);
}
while ((prefix = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
{
const ContextTlv *contextTlv = prefix->FindSubTlv<ContextTlv>();
if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId))
{
continue;
}
aContext.mPrefix.Set(prefix->GetPrefix(), prefix->GetPrefixLength());
aContext.mContextId = contextTlv->GetContextId();
aContext.mCompressFlag = contextTlv->IsCompress();
ExitNow(error = kErrorNone);
}
exit:
return error;
}
bool LeaderBase::IsOnMesh(const Ip6::Address &aAddress) const
{
const PrefixTlv *prefix = nullptr;
bool rval = false;
VerifyOrExit(!Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress), rval = true);
while ((prefix = FindNextMatchingPrefix(aAddress, prefix)) != nullptr)
{
// check both stable and temporary Border Router TLVs
for (int i = 0; i < 2; i++)
{
const BorderRouterTlv *borderRouter = prefix->FindSubTlv<BorderRouterTlv>(/* aStable */ (i == 0));
if (borderRouter == nullptr)
{
continue;
}
for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
entry = entry->GetNext())
{
if (entry->IsOnMesh())
{
ExitNow(rval = true);
}
}
}
}
exit:
return rval;
}
Error LeaderBase::RouteLookup(const Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t * aPrefixMatchLength,
uint16_t * aRloc16) const
{
Error error = kErrorNoRoute;
const PrefixTlv *prefix = nullptr;
while ((prefix = FindNextMatchingPrefix(aSource, prefix)) != nullptr)
{
if (ExternalRouteLookup(prefix->GetDomainId(), aDestination, aPrefixMatchLength, aRloc16) == kErrorNone)
{
ExitNow(error = kErrorNone);
}
if (DefaultRouteLookup(*prefix, aRloc16) == kErrorNone)
{
if (aPrefixMatchLength)
{
*aPrefixMatchLength = 0;
}
ExitNow(error = kErrorNone);
}
}
exit:
return error;
}
Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId,
const Ip6::Address &aDestination,
uint8_t * aPrefixMatchLength,
uint16_t * aRloc16) const
{
Error error = kErrorNoRoute;
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
const PrefixTlv * prefixTlv;
const HasRouteEntry *bestRouteEntry = nullptr;
uint8_t bestMatchLength = 0;
while ((prefixTlv = tlvIterator.Iterate<PrefixTlv>()) != nullptr)
{
const HasRouteTlv *hasRoute;
uint8_t prefixLength = prefixTlv->GetPrefixLength();
TlvIterator subTlvIterator(*prefixTlv);
if (prefixTlv->GetDomainId() != aDomainId)
{
continue;
}
if (!aDestination.MatchesPrefix(prefixTlv->GetPrefix(), prefixLength))
{
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 || entry->GetPreference() > bestRouteEntry->GetPreference() ||
(entry->GetPreference() == bestRouteEntry->GetPreference() &&
(entry->GetRloc() == Get<Mle::MleRouter>().GetRloc16() ||
(bestRouteEntry->GetRloc() != Get<Mle::MleRouter>().GetRloc16() &&
Get<Mle::MleRouter>().GetCost(entry->GetRloc()) <
Get<Mle::MleRouter>().GetCost(bestRouteEntry->GetRloc())))))
{
bestRouteEntry = entry;
bestMatchLength = prefixLength;
}
}
}
}
if (bestRouteEntry != nullptr)
{
if (aRloc16 != nullptr)
{
*aRloc16 = bestRouteEntry->GetRloc();
}
if (aPrefixMatchLength != nullptr)
{
*aPrefixMatchLength = bestMatchLength;
}
error = kErrorNone;
}
return error;
}
Error LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t *aRloc16) const
{
Error error = kErrorNoRoute;
TlvIterator subTlvIterator(aPrefix);
const BorderRouterTlv * borderRouter;
const BorderRouterEntry *route = nullptr;
while ((borderRouter = subTlvIterator.Iterate<BorderRouterTlv>()) != nullptr)
{
for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
entry = entry->GetNext())
{
if (!entry->IsDefaultRoute())
{
continue;
}
if (route == nullptr || entry->GetPreference() > route->GetPreference() ||
(entry->GetPreference() == route->GetPreference() &&
(entry->GetRloc() == Get<Mle::MleRouter>().GetRloc16() ||
(route->GetRloc() != Get<Mle::MleRouter>().GetRloc16() &&
Get<Mle::MleRouter>().GetCost(entry->GetRloc()) < Get<Mle::MleRouter>().GetCost(route->GetRloc())))))
{
route = entry;
}
}
}
if (route != nullptr)
{
if (aRloc16 != nullptr)
{
*aRloc16 = route->GetRloc();
}
error = kErrorNone;
}
return error;
}
Error LeaderBase::SetNetworkData(uint8_t aVersion,
uint8_t aStableVersion,
Type aType,
const Message &aMessage,
uint16_t aMessageOffset)
{
Error error = kErrorNone;
Mle::Tlv tlv;
uint16_t length;
SuccessOrExit(error = aMessage.Read(aMessageOffset, tlv));
length = aMessage.ReadBytes(aMessageOffset + sizeof(tlv), GetBytes(), tlv.GetLength());
VerifyOrExit(length == tlv.GetLength(), error = kErrorParse);
SetLength(tlv.GetLength());
mVersion = aVersion;
mStableVersion = aStableVersion;
if (aType == kStableSubset)
{
RemoveTemporaryData();
}
#if OPENTHREAD_FTD
// Synchronize internal 6LoWPAN Context ID Set with recently obtained Network Data.
if (Get<Mle::MleRouter>().IsLeader())
{
Get<Leader>().UpdateContextsAfterReset();
}
#endif
DumpDebg("SetNetworkData", GetBytes(), GetLength());
Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
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++;
Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
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);
}
} // namespace NetworkData
} // namespace ot