blob: 4497f3797c797d067b4a95cd99cf6f3229238df7 [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/thread_uri_paths.hpp"
namespace ot {
namespace NetworkData {
LeaderBase::LeaderBase(Instance &aInstance)
: NetworkData(aInstance, kTypeLeader)
{
Reset();
}
void LeaderBase::Reset(void)
{
mVersion = Random::NonCrypto::GetUint8();
mStableVersion = Random::NonCrypto::GetUint8();
mLength = 0;
Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
}
otError LeaderBase::GetServiceId(uint32_t aEnterpriseNumber,
const uint8_t *aServiceData,
uint8_t aServiceDataLength,
bool aServerStable,
uint8_t & aServiceId) const
{
otError error = OT_ERROR_NOT_FOUND;
Iterator iterator = kIteratorInit;
otServiceConfig serviceConfig;
while (GetNextService(iterator, serviceConfig) == OT_ERROR_NONE)
{
if (aEnterpriseNumber == serviceConfig.mEnterpriseNumber &&
aServiceDataLength == serviceConfig.mServiceDataLength &&
memcmp(aServiceData, serviceConfig.mServiceData, aServiceDataLength) == 0 &&
aServerStable == serviceConfig.mServerConfig.mStable)
{
aServiceId = serviceConfig.mServiceId;
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
return error;
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
otError LeaderBase::GetBackboneRouterPrimary(BackboneRouter::BackboneRouterConfig &aConfig) const
{
otError error = OT_ERROR_NOT_FOUND;
uint8_t serviceData = ServiceTlv::kServiceDataBackboneRouter;
const ServerTlv * rvalServerTlv = nullptr;
const BackboneRouterServerData *rvalServerData = nullptr;
const ServiceTlv * serviceTlv;
const ServerTlv * serverTlv;
serviceTlv = Get<Leader>().FindService(ServiceTlv::kThreadEnterpriseNumber, &serviceData, sizeof(serviceData));
VerifyOrExit(serviceTlv != nullptr, aConfig.mServer16 = Mac::kShortAddrInvalid);
for (const NetworkDataTlv *start = serviceTlv->GetSubTlvs();
(serverTlv = FindTlv<ServerTlv>(start, serviceTlv->GetNext())) != nullptr; start = serverTlv->GetNext())
{
const BackboneRouterServerData *serverData =
reinterpret_cast<const BackboneRouterServerData *>(serverTlv->GetServerData());
if (rvalServerTlv == nullptr ||
(serverTlv->GetServer16() == Mle::Mle::Rloc16FromRouterId(Get<Mle::MleRouter>().GetLeaderId())) ||
serverData->GetSequenceNumber() > rvalServerData->GetSequenceNumber() ||
(serverData->GetSequenceNumber() == rvalServerData->GetSequenceNumber() &&
serverTlv->GetServer16() > rvalServerTlv->GetServer16()))
{
rvalServerTlv = serverTlv;
rvalServerData = serverData;
}
}
VerifyOrExit(rvalServerTlv != nullptr, OT_NOOP);
aConfig.mServer16 = rvalServerTlv->GetServer16();
aConfig.mSequenceNumber = rvalServerData->GetSequenceNumber();
aConfig.mReregistrationDelay = rvalServerData->GetReregistrationDelay();
aConfig.mMlrTimeout = rvalServerData->GetMlrTimeout();
error = OT_ERROR_NONE;
exit:
return error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
const PrefixTlv *LeaderBase::FindNextMatchingPrefix(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
{
const PrefixTlv *prefix;
for (const NetworkDataTlv *start = (aPrevTlv == nullptr) ? GetTlvsStart() : aPrevTlv->GetNext();
(prefix = FindTlv<PrefixTlv>(start, GetTlvsEnd())) != nullptr; start = prefix->GetNext())
{
if (PrefixMatch(prefix->GetPrefix(), aAddress.mFields.m8, prefix->GetPrefixLength()) >= 0)
{
ExitNow();
}
}
exit:
return prefix;
}
otError LeaderBase::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext) const
{
const PrefixTlv * prefix = nullptr;
const ContextTlv *contextTlv;
aContext.mPrefixLength = 0;
if (Get<Mle::MleRouter>().IsMeshLocalAddress(aAddress))
{
aContext.mPrefix = Get<Mle::MleRouter>().GetMeshLocalPrefix().m8;
aContext.mPrefixLength = Mle::MeshLocalPrefix::kLength;
aContext.mContextId = Mle::kMeshLocalPrefixContextId;
aContext.mCompressFlag = true;
}
while ((prefix = FindNextMatchingPrefix(aAddress, prefix)) != nullptr)
{
contextTlv = FindContext(*prefix);
if (contextTlv == nullptr)
{
continue;
}
if (prefix->GetPrefixLength() > aContext.mPrefixLength)
{
aContext.mPrefix = prefix->GetPrefix();
aContext.mPrefixLength = prefix->GetPrefixLength();
aContext.mContextId = contextTlv->GetContextId();
aContext.mCompressFlag = contextTlv->IsCompress();
}
}
return (aContext.mPrefixLength > 0) ? OT_ERROR_NONE : OT_ERROR_NOT_FOUND;
}
otError LeaderBase::GetContext(uint8_t aContextId, Lowpan::Context &aContext) const
{
otError error = OT_ERROR_NOT_FOUND;
const PrefixTlv *prefix;
if (aContextId == Mle::kMeshLocalPrefixContextId)
{
aContext.mPrefix = Get<Mle::MleRouter>().GetMeshLocalPrefix().m8;
aContext.mPrefixLength = Mle::MeshLocalPrefix::kLength;
aContext.mContextId = Mle::kMeshLocalPrefixContextId;
aContext.mCompressFlag = true;
ExitNow(error = OT_ERROR_NONE);
}
for (const NetworkDataTlv *start = GetTlvsStart(); (prefix = FindTlv<PrefixTlv>(start, GetTlvsEnd())) != nullptr;
start = prefix->GetNext())
{
const ContextTlv *contextTlv = FindContext(*prefix);
if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId))
{
continue;
}
aContext.mPrefix = prefix->GetPrefix();
aContext.mPrefixLength = prefix->GetPrefixLength();
aContext.mContextId = contextTlv->GetContextId();
aContext.mCompressFlag = contextTlv->IsCompress();
ExitNow(error = OT_ERROR_NONE);
}
exit:
return error;
}
otError LeaderBase::GetRlocByContextId(uint8_t aContextId, uint16_t &aRloc16) const
{
otError error = OT_ERROR_NOT_FOUND;
Lowpan::Context lowpanContext;
if ((GetContext(aContextId, lowpanContext)) == OT_ERROR_NONE)
{
Iterator iterator = kIteratorInit;
OnMeshPrefixConfig config;
while (GetNextOnMeshPrefix(iterator, config) == OT_ERROR_NONE)
{
if (otIp6PrefixMatch(&(config.mPrefix.mPrefix), reinterpret_cast<const otIp6Address *>(
lowpanContext.mPrefix)) >= config.mPrefix.mLength)
{
aRloc16 = config.mRloc16;
ExitNow(error = OT_ERROR_NONE);
}
}
}
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 = FindBorderRouter(*prefix, /* 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;
}
otError LeaderBase::RouteLookup(const Ip6::Address &aSource,
const Ip6::Address &aDestination,
uint8_t * aPrefixMatch,
uint16_t * aRloc16) const
{
otError error = OT_ERROR_NO_ROUTE;
const PrefixTlv *prefix = nullptr;
while ((prefix = FindNextMatchingPrefix(aSource, prefix)) != nullptr)
{
if (ExternalRouteLookup(prefix->GetDomainId(), aDestination, aPrefixMatch, aRloc16) == OT_ERROR_NONE)
{
ExitNow(error = OT_ERROR_NONE);
}
if (DefaultRouteLookup(*prefix, aRloc16) == OT_ERROR_NONE)
{
if (aPrefixMatch)
{
*aPrefixMatch = 0;
}
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
return error;
}
otError LeaderBase::ExternalRouteLookup(uint8_t aDomainId,
const Ip6::Address &aDestination,
uint8_t * aPrefixMatch,
uint16_t * aRloc16) const
{
otError error = OT_ERROR_NO_ROUTE;
const PrefixTlv * prefix;
const HasRouteEntry *rvalRoute = nullptr;
uint8_t rval_plen = 0;
for (const NetworkDataTlv *start = GetTlvsStart(); (prefix = FindTlv<PrefixTlv>(start, GetTlvsEnd())) != nullptr;
start = prefix->GetNext())
{
const HasRouteTlv *hasRoute;
int8_t plen;
if (prefix->GetDomainId() != aDomainId)
{
continue;
}
plen = PrefixMatch(prefix->GetPrefix(), aDestination.mFields.m8, prefix->GetPrefixLength());
if (plen <= rval_plen)
{
continue;
}
for (const NetworkDataTlv *subStart = prefix->GetSubTlvs();
(hasRoute = FindTlv<HasRouteTlv>(subStart, prefix->GetNext())) != nullptr; subStart = hasRoute->GetNext())
{
for (const HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
entry = entry->GetNext())
{
if (rvalRoute == nullptr || entry->GetPreference() > rvalRoute->GetPreference() ||
(entry->GetPreference() == rvalRoute->GetPreference() &&
(entry->GetRloc() == Get<Mle::MleRouter>().GetRloc16() ||
(rvalRoute->GetRloc() != Get<Mle::MleRouter>().GetRloc16() &&
Get<Mle::MleRouter>().GetCost(entry->GetRloc()) <
Get<Mle::MleRouter>().GetCost(rvalRoute->GetRloc())))))
{
rvalRoute = entry;
rval_plen = static_cast<uint8_t>(plen);
}
}
}
}
if (rvalRoute != nullptr)
{
if (aRloc16 != nullptr)
{
*aRloc16 = rvalRoute->GetRloc();
}
if (aPrefixMatch != nullptr)
{
*aPrefixMatch = rval_plen;
}
error = OT_ERROR_NONE;
}
return error;
}
otError LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t *aRloc16) const
{
otError error = OT_ERROR_NO_ROUTE;
const BorderRouterTlv * borderRouter;
const BorderRouterEntry *route = nullptr;
for (const NetworkDataTlv *start = aPrefix.GetSubTlvs();
(borderRouter = FindTlv<BorderRouterTlv>(start, aPrefix.GetNext())) != nullptr;
start = borderRouter->GetNext())
{
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 = OT_ERROR_NONE;
}
return error;
}
otError LeaderBase::SetNetworkData(uint8_t aVersion,
uint8_t aStableVersion,
bool aStableOnly,
const Message &aMessage,
uint16_t aMessageOffset)
{
otError error = OT_ERROR_NONE;
Mle::Tlv tlv;
uint16_t length;
length = aMessage.Read(aMessageOffset, sizeof(tlv), &tlv);
VerifyOrExit(length == sizeof(tlv), error = OT_ERROR_PARSE);
length = aMessage.Read(aMessageOffset + sizeof(tlv), tlv.GetLength(), mTlvs);
VerifyOrExit(length == tlv.GetLength(), error = OT_ERROR_PARSE);
mLength = tlv.GetLength();
mVersion = aVersion;
mStableVersion = aStableVersion;
if (aStableOnly)
{
RemoveTemporaryData(mTlvs, mLength);
}
#if OPENTHREAD_FTD
// Synchronize internal 6LoWPAN Context ID Set with recently obtained Network Data.
if (Get<Mle::MleRouter>().IsLeader())
{
Get<Leader>().UpdateContextsAfterReset();
}
#endif
otDumpDebgNetData("set network data", mTlvs, mLength);
Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
exit:
return error;
}
otError LeaderBase::SetCommissioningData(const uint8_t *aValue, uint8_t aValueLength)
{
otError error = OT_ERROR_NONE;
CommissioningDataTlv *commissioningDataTlv;
RemoveCommissioningData();
if (aValueLength > 0)
{
VerifyOrExit(aValueLength <= kMaxSize - sizeof(CommissioningDataTlv), error = OT_ERROR_NO_BUFS);
commissioningDataTlv =
static_cast<CommissioningDataTlv *>(AppendTlv(sizeof(CommissioningDataTlv) + aValueLength));
VerifyOrExit(commissioningDataTlv != nullptr, error = OT_ERROR_NO_BUFS);
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 FindTlv<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, OT_NOOP);
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, OT_NOOP);
steeringData = GetCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData);
VerifyOrExit(steeringData != nullptr, OT_NOOP);
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, OT_NOOP);
RemoveTlv(tlv);
exit:
return;
}
} // namespace NetworkData
} // namespace ot