blob: 0575d19b97ead5341be6c835e93efa6f09308d0e [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.
*/
#define WPP_NAME "network_data_leader.tmh"
#include <openthread/config.h>
#include "network_data_leader.hpp"
#include <openthread/platform/random.h>
#include "coap/coap_header.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/logging.hpp"
#include "common/message.hpp"
#include "common/timer.hpp"
#include "mac/mac_frame.hpp"
#include "thread/mle_router.hpp"
#include "thread/lowpan.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_tlvs.hpp"
#include "thread/thread_uri_paths.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
namespace NetworkData {
LeaderBase::LeaderBase(ThreadNetif &aThreadNetif):
NetworkData(aThreadNetif, false)
{
Reset();
}
void LeaderBase::Reset(void)
{
mVersion = static_cast<uint8_t>(otPlatRandomGet());
mStableVersion = static_cast<uint8_t>(otPlatRandomGet());
mLength = 0;
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_NETDATA);
}
otError LeaderBase::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext)
{
ThreadNetif &netif = GetNetif();
PrefixTlv *prefix;
ContextTlv *contextTlv;
aContext.mPrefixLength = 0;
if (PrefixMatch(netif.GetMle().GetMeshLocalPrefix(), aAddress.mFields.m8, 64) >= 0)
{
aContext.mPrefix = netif.GetMle().GetMeshLocalPrefix();
aContext.mPrefixLength = 64;
aContext.mContextId = 0;
aContext.mCompressFlag = true;
}
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
if (PrefixMatch(prefix->GetPrefix(), aAddress.mFields.m8, prefix->GetPrefixLength()) < 0)
{
continue;
}
contextTlv = FindContext(*prefix);
if (contextTlv == NULL)
{
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)
{
otError error = OT_ERROR_NOT_FOUND;
PrefixTlv *prefix;
ContextTlv *contextTlv;
if (aContextId == 0)
{
aContext.mPrefix = GetNetif().GetMle().GetMeshLocalPrefix();
aContext.mPrefixLength = 64;
aContext.mContextId = 0;
aContext.mCompressFlag = true;
ExitNow(error = OT_ERROR_NONE);
}
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
contextTlv = FindContext(*prefix);
if (contextTlv == NULL)
{
continue;
}
if (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;
}
#if OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT
otError LeaderBase::GetRlocByContextId(uint8_t aContextId, uint16_t &aRloc16)
{
otError error = OT_ERROR_NOT_FOUND;
Lowpan::Context lowpanContext;
if ((GetContext(aContextId, lowpanContext)) == OT_ERROR_NONE)
{
otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
otBorderRouterConfig 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;
}
#endif // OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT
bool LeaderBase::IsOnMesh(const Ip6::Address &aAddress)
{
PrefixTlv *prefix;
bool rval = false;
if (memcmp(aAddress.mFields.m8, GetNetif().GetMle().GetMeshLocalPrefix(), 8) == 0)
{
ExitNow(rval = true);
}
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
if (PrefixMatch(prefix->GetPrefix(), aAddress.mFields.m8, prefix->GetPrefixLength()) < 0)
{
continue;
}
if (FindBorderRouter(*prefix) == NULL)
{
continue;
}
ExitNow(rval = true);
}
exit:
return rval;
}
otError LeaderBase::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination,
uint8_t *aPrefixMatch, uint16_t *aRloc16)
{
otError error = OT_ERROR_NO_ROUTE;
PrefixTlv *prefix;
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
if (PrefixMatch(prefix->GetPrefix(), aSource.mFields.m8, prefix->GetPrefixLength()) >= 0)
{
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)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NO_ROUTE;
PrefixTlv *prefix;
HasRouteTlv *hasRoute;
HasRouteEntry *entry;
HasRouteEntry *rvalRoute = NULL;
uint8_t rval_plen = 0;
int8_t plen;
NetworkDataTlv *cur;
NetworkDataTlv *subCur;
for (cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
if (prefix->GetDomainId() != aDomainId)
{
continue;
}
plen = PrefixMatch(prefix->GetPrefix(), aDestination.mFields.m8, prefix->GetPrefixLength());
if (plen > rval_plen)
{
// select border router
for (subCur = prefix->GetSubTlvs(); subCur < prefix->GetNext(); subCur = subCur->GetNext())
{
if (subCur->GetType() != NetworkDataTlv::kTypeHasRoute)
{
continue;
}
hasRoute = static_cast<HasRouteTlv *>(subCur);
for (uint8_t i = 0; i < hasRoute->GetNumEntries(); i++)
{
entry = hasRoute->GetEntry(i);
if (rvalRoute == NULL ||
entry->GetPreference() > rvalRoute->GetPreference() ||
(entry->GetPreference() == rvalRoute->GetPreference() &&
netif.GetMle().GetCost(entry->GetRloc()) < netif.GetMle().GetCost(rvalRoute->GetRloc())))
{
rvalRoute = entry;
rval_plen = static_cast<uint8_t>(plen);
}
}
}
}
}
if (rvalRoute != NULL)
{
if (aRloc16 != NULL)
{
*aRloc16 = rvalRoute->GetRloc();
}
if (aPrefixMatch != NULL)
{
*aPrefixMatch = rval_plen;
}
error = OT_ERROR_NONE;
}
return error;
}
otError LeaderBase::DefaultRouteLookup(PrefixTlv &aPrefix, uint16_t *aRloc16)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NO_ROUTE;
BorderRouterTlv *borderRouter;
BorderRouterEntry *entry;
BorderRouterEntry *route = NULL;
for (NetworkDataTlv *cur = aPrefix.GetSubTlvs(); cur < aPrefix.GetNext(); cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypeBorderRouter)
{
continue;
}
borderRouter = static_cast<BorderRouterTlv *>(cur);
for (uint8_t i = 0; i < borderRouter->GetNumEntries(); i++)
{
entry = borderRouter->GetEntry(i);
if (entry->IsDefaultRoute() == false)
{
continue;
}
if (route == NULL ||
entry->GetPreference() > route->GetPreference() ||
(entry->GetPreference() == route->GetPreference() &&
(entry->GetRloc() == netif.GetMle().GetRloc16() ||
(route->GetRloc() != netif.GetMle().GetRloc16() &&
netif.GetMle().GetCost(entry->GetRloc()) < netif.GetMle().GetCost(route->GetRloc())))))
{
route = entry;
}
}
}
if (route != NULL)
{
if (aRloc16 != NULL)
{
*aRloc16 = route->GetRloc();
}
error = OT_ERROR_NONE;
}
return error;
}
void LeaderBase::SetNetworkData(uint8_t aVersion, uint8_t aStableVersion, bool aStable,
const uint8_t *aData, uint8_t aDataLength)
{
mVersion = aVersion;
mStableVersion = aStableVersion;
memcpy(mTlvs, aData, aDataLength);
mLength = aDataLength;
if (aStable)
{
RemoveTemporaryData(mTlvs, mLength);
}
otDumpDebgNetData(GetInstance(), "set network data", mTlvs, mLength);
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_NETDATA);
}
otError LeaderBase::SetCommissioningData(const uint8_t *aValue, uint8_t aValueLength)
{
otError error = OT_ERROR_NONE;
uint8_t remaining = kMaxSize - mLength;
CommissioningDataTlv *commissioningDataTlv;
VerifyOrExit(sizeof(NetworkDataTlv) + aValueLength < remaining, error = OT_ERROR_NO_BUFS);
RemoveCommissioningData();
if (aValueLength > 0)
{
commissioningDataTlv = reinterpret_cast<CommissioningDataTlv *>(mTlvs + mLength);
Insert(reinterpret_cast<uint8_t *>(commissioningDataTlv), sizeof(CommissioningDataTlv) + aValueLength);
commissioningDataTlv->Init();
commissioningDataTlv->SetLength(aValueLength);
memcpy(commissioningDataTlv->GetValue(), aValue, aValueLength);
}
mVersion++;
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_NETDATA);
exit:
return error;
}
NetworkDataTlv *LeaderBase::GetCommissioningData(void)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext())
{
if (cur->GetType() == NetworkDataTlv::kTypeCommissioningData)
{
ExitNow();
}
}
cur = NULL;
exit:
return cur;
}
MeshCoP::Tlv *LeaderBase::GetCommissioningDataSubTlv(MeshCoP::Tlv::Type aType)
{
MeshCoP::Tlv *rval = NULL;
NetworkDataTlv *commissioningDataTlv;
MeshCoP::Tlv *cur;
MeshCoP::Tlv *end;
commissioningDataTlv = GetCommissioningData();
VerifyOrExit(commissioningDataTlv != NULL);
cur = reinterpret_cast<MeshCoP::Tlv *>(commissioningDataTlv->GetValue());
end = reinterpret_cast<MeshCoP::Tlv *>(commissioningDataTlv->GetValue() + commissioningDataTlv->GetLength());
for (; cur < end; cur = cur->GetNext())
{
if (cur->GetType() == aType)
{
ExitNow(rval = cur);
}
}
exit:
return rval;
}
bool LeaderBase::IsJoiningEnabled(void)
{
MeshCoP::Tlv *steeringData;
bool rval = false;
VerifyOrExit(GetCommissioningDataSubTlv(MeshCoP::Tlv::kBorderAgentLocator) != NULL);
steeringData = GetCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData);
VerifyOrExit(steeringData != NULL);
for (int i = 0; i < steeringData->GetLength(); i++)
{
if (steeringData->GetValue()[i] != 0)
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
otError LeaderBase::RemoveCommissioningData(void)
{
otError error = OT_ERROR_NOT_FOUND;
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() == NetworkDataTlv::kTypeCommissioningData)
{
Remove(reinterpret_cast<uint8_t *>(cur), sizeof(NetworkDataTlv) + cur->GetLength());
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
return error;
}
} // namespace NetworkData
} // namespace ot