blob: 3a183a1bc0306ff7db721641bb8ab8371f26e40f [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 <coap/coap_header.hpp>
#include <common/debug.hpp>
#include <common/logging.hpp>
#include <common/code_utils.hpp>
#include <common/encoding.hpp>
#include <common/message.hpp>
#include <common/timer.hpp>
#include <mac/mac_frame.hpp>
#include <platform/random.h>
#include <thread/mle_router.hpp>
#include <thread/network_data_leader.hpp>
#include <thread/thread_netif.hpp>
#include <thread/thread_tlvs.hpp>
#include <thread/thread_uris.hpp>
using Thread::Encoding::BigEndian::HostSwap16;
namespace Thread {
namespace NetworkData {
Leader::Leader(ThreadNetif &aThreadNetif):
NetworkData(aThreadNetif),
mTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleTimer, this),
mServerData(OPENTHREAD_URI_SERVER_DATA, &HandleServerData, this),
mCoapServer(aThreadNetif.GetCoapServer()),
mNetif(aThreadNetif)
{
Reset();
}
void Leader::Reset(void)
{
memset(mContextLastUsed, 0, sizeof(mContextLastUsed));
mVersion = static_cast<uint8_t>(otPlatRandomGet());
mStableVersion = static_cast<uint8_t>(otPlatRandomGet());
mLength = 0;
mContextUsed = 0;
mContextIdReuseDelay = kContextIdReuseDelay;
mNetif.SetStateChangedFlags(OT_THREAD_NETDATA_UPDATED);
}
void Leader::Start(void)
{
mCoapServer.AddResource(mServerData);
}
void Leader::Stop(void)
{
}
uint8_t Leader::GetVersion(void) const
{
return mVersion;
}
void Leader::IncrementVersion(void)
{
if (mMle.GetDeviceState() == Mle::kDeviceStateLeader)
{
mVersion++;
}
}
uint8_t Leader::GetStableVersion(void) const
{
return mStableVersion;
}
void Leader::IncrementStableVersion(void)
{
if (mMle.GetDeviceState() == Mle::kDeviceStateLeader)
{
mStableVersion++;
}
}
uint32_t Leader::GetContextIdReuseDelay(void) const
{
return mContextIdReuseDelay;
}
ThreadError Leader::SetContextIdReuseDelay(uint32_t aDelay)
{
mContextIdReuseDelay = aDelay;
return kThreadError_None;
}
ThreadError Leader::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext)
{
PrefixTlv *prefix;
ContextTlv *contextTlv;
aContext.mPrefixLength = 0;
if (PrefixMatch(mMle.GetMeshLocalPrefix(), aAddress.mFields.m8, 64) >= 0)
{
aContext.mPrefix = mMle.GetMeshLocalPrefix();
aContext.mPrefixLength = 64;
aContext.mContextId = 0;
}
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = reinterpret_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();
}
}
return (aContext.mPrefixLength > 0) ? kThreadError_None : kThreadError_Error;
}
ThreadError Leader::GetContext(uint8_t aContextId, Lowpan::Context &aContext)
{
ThreadError error = kThreadError_Error;
PrefixTlv *prefix;
ContextTlv *contextTlv;
if (aContextId == 0)
{
aContext.mPrefix = mMle.GetMeshLocalPrefix();
aContext.mPrefixLength = 64;
aContext.mContextId = 0;
ExitNow(error = kThreadError_None);
}
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = reinterpret_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();
ExitNow(error = kThreadError_None);
}
exit:
return error;
}
bool Leader::IsOnMesh(const Ip6::Address &aAddress)
{
PrefixTlv *prefix;
bool rval = false;
if (memcmp(aAddress.mFields.m8, mMle.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 = reinterpret_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;
}
ThreadError Leader::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination,
uint8_t *aPrefixMatch, uint16_t *aRloc16)
{
ThreadError error = kThreadError_NoRoute;
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 = reinterpret_cast<PrefixTlv *>(cur);
if (PrefixMatch(prefix->GetPrefix(), aSource.mFields.m8, prefix->GetPrefixLength()) >= 0)
{
if (ExternalRouteLookup(prefix->GetDomainId(), aDestination, aPrefixMatch, aRloc16) == kThreadError_None)
{
ExitNow(error = kThreadError_None);
}
if (DefaultRouteLookup(*prefix, aRloc16) == kThreadError_None)
{
if (aPrefixMatch)
{
*aPrefixMatch = 0;
}
ExitNow(error = kThreadError_None);
}
}
}
exit:
return error;
}
ThreadError Leader::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination,
uint8_t *aPrefixMatch, uint16_t *aRloc16)
{
ThreadError error = kThreadError_NoRoute;
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 = reinterpret_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 = reinterpret_cast<NetworkDataTlv *>(prefix->GetSubTlvs());
subCur < reinterpret_cast<NetworkDataTlv *>(prefix->GetSubTlvs() + prefix->GetSubTlvsLength());
subCur = subCur->GetNext())
{
if (subCur->GetType() != NetworkDataTlv::kTypeHasRoute)
{
continue;
}
hasRoute = reinterpret_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() &&
mMle.GetRouteCost(entry->GetRloc()) < mMle.GetRouteCost(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 = kThreadError_None;
}
return error;
}
ThreadError Leader::DefaultRouteLookup(PrefixTlv &aPrefix, uint16_t *aRloc16)
{
ThreadError error = kThreadError_NoRoute;
BorderRouterTlv *borderRouter;
BorderRouterEntry *entry;
BorderRouterEntry *route = NULL;
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
cur < reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
cur = cur->GetNext())
{
if (cur->GetType() != NetworkDataTlv::kTypeBorderRouter)
{
continue;
}
borderRouter = reinterpret_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() &&
mMle.GetRouteCost(entry->GetRloc()) < mMle.GetRouteCost(route->GetRloc())))
{
route = entry;
}
}
}
if (route != NULL)
{
if (aRloc16 != NULL)
{
*aRloc16 = route->GetRloc();
}
error = kThreadError_None;
}
return error;
}
void Leader::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("set network data", mTlvs, mLength);
mNetif.SetStateChangedFlags(OT_THREAD_NETDATA_UPDATED);
}
void Leader::RemoveBorderRouter(uint16_t aRloc16)
{
bool rlocIn = false;
bool rlocStable = false;
RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength);
VerifyOrExit(rlocIn, ;);
RemoveRloc(aRloc16);
mVersion++;
if (rlocStable)
{
mStableVersion++;
}
mNetif.SetStateChangedFlags(OT_THREAD_NETDATA_UPDATED);
exit:
return;
}
ThreadError Leader::SetCommissioningData(const uint8_t *aValue, uint8_t aValueLength)
{
ThreadError error = kThreadError_None;
uint8_t remaining = kMaxSize - mLength;
CommissioningDataTlv *commissioningDataTlv;
VerifyOrExit(sizeof(NetworkDataTlv) + aValueLength < remaining, error = kThreadError_NoBufs);
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++;
mNetif.SetStateChangedFlags(OT_THREAD_NETDATA_UPDATED);
exit:
return error;
}
uint8_t *Leader::GetCommissioningData(uint8_t &aLength)
{
uint8_t *rval = NULL;
for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
cur = cur->GetNext())
{
if (cur->GetType() == NetworkDataTlv::kTypeCommissioningData)
{
aLength = cur->GetLength();
ExitNow(rval = cur->GetValue());
}
}
exit:
return rval;
}
ThreadError Leader::RemoveCommissioningData(void)
{
ThreadError error = kThreadError_NotFound;
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 = kThreadError_None);
}
}
exit:
return error;
}
void Leader::HandleServerData(void *aContext, Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo)
{
Leader *obj = reinterpret_cast<Leader *>(aContext);
obj->HandleServerData(aHeader, aMessage, aMessageInfo);
}
void Leader::HandleServerData(Coap::Header &aHeader, Message &aMessage,
const Ip6::MessageInfo &aMessageInfo)
{
ThreadNetworkDataTlv networkData;
ThreadRloc16Tlv rloc16;
otLogInfoNetData("Received network data registration\n");
if (ThreadTlv::GetTlv(aMessage, ThreadTlv::kRloc16, sizeof(rloc16), rloc16) == kThreadError_None)
{
RemoveBorderRouter(rloc16.GetRloc16());
}
if (ThreadTlv::GetTlv(aMessage, ThreadTlv::kThreadNetworkData, sizeof(networkData), networkData) ==
kThreadError_None)
{
RegisterNetworkData(HostSwap16(aMessageInfo.mPeerAddr.mFields.m16[7]),
networkData.GetTlvs(), networkData.GetLength());
}
SendServerDataResponse(aHeader, aMessageInfo, NULL, 0);
}
void Leader::SendServerDataResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aMessageInfo,
const uint8_t *aTlvs, uint8_t aTlvsLength)
{
ThreadError error = kThreadError_None;
Coap::Header responseHeader;
Message *message;
VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
responseHeader.Init();
responseHeader.SetVersion(1);
responseHeader.SetType(Coap::Header::kTypeAcknowledgment);
responseHeader.SetCode(Coap::Header::kCodeChanged);
responseHeader.SetMessageId(aRequestHeader.GetMessageId());
responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength());
responseHeader.AppendContentFormatOption(Coap::Header::kApplicationOctetStream);
responseHeader.Finalize();
SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength()));
SuccessOrExit(error = message->Append(aTlvs, aTlvsLength));
SuccessOrExit(error = mCoapServer.SendMessage(*message, aMessageInfo));
otLogInfoNetData("Sent network data registration acknowledgment\n");
exit:
if (error != kThreadError_None && message != NULL)
{
message->Free();
}
}
void Leader::RlocLookup(uint16_t aRloc16, bool &aIn, bool &aStable, uint8_t *aTlvs, uint8_t aTlvsLength)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aTlvs + aTlvsLength);
NetworkDataTlv *subCur;
NetworkDataTlv *subEnd;
PrefixTlv *prefix;
BorderRouterTlv *borderRouter;
HasRouteTlv *hasRoute;
BorderRouterEntry *borderRouterEntry;
HasRouteEntry *hasRouteEntry;
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypePrefix)
{
prefix = reinterpret_cast<PrefixTlv *>(cur);
subCur = reinterpret_cast<NetworkDataTlv *>(prefix->GetSubTlvs());
subEnd = reinterpret_cast<NetworkDataTlv *>(prefix->GetSubTlvs() + prefix->GetSubTlvsLength());
while (subCur < subEnd)
{
switch (subCur->GetType())
{
case NetworkDataTlv::kTypeBorderRouter:
borderRouter = FindBorderRouter(*prefix);
for (uint8_t i = 0; i < borderRouter->GetNumEntries(); i++)
{
borderRouterEntry = borderRouter->GetEntry(i);
if (borderRouterEntry->GetRloc() == aRloc16)
{
aIn = true;
if (borderRouter->IsStable())
{
aStable = true;
}
}
}
break;
case NetworkDataTlv::kTypeHasRoute:
hasRoute = FindHasRoute(*prefix);
for (uint8_t i = 0; i < hasRoute->GetNumEntries(); i++)
{
hasRouteEntry = hasRoute->GetEntry(i);
if (hasRouteEntry->GetRloc() == aRloc16)
{
aIn = true;
if (hasRoute->IsStable())
{
aStable = true;
}
}
}
break;
default:
break;
}
if (aIn && aStable)
{
ExitNow();
}
subCur = subCur->GetNext();
}
}
cur = cur->GetNext();
}
exit:
return;
}
bool Leader::IsStableUpdated(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aTlvsBase,
uint8_t aTlvsBaseLength)
{
bool rval = false;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aTlvs + aTlvsLength);
PrefixTlv *prefix;
PrefixTlv *prefixBase;
BorderRouterTlv *borderRouter;
HasRouteTlv *hasRoute;
ContextTlv *context;
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypePrefix)
{
prefix = reinterpret_cast<PrefixTlv *>(cur);
context = FindContext(*prefix);
borderRouter = FindBorderRouter(*prefix);
hasRoute = FindHasRoute(*prefix);
if (cur->IsStable() && (!context || borderRouter))
{
prefixBase = FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength(), aTlvsBase, aTlvsBaseLength);
if (!prefixBase)
{
ExitNow(rval = true);
}
if (borderRouter && memcmp(borderRouter, FindBorderRouter(*prefixBase), borderRouter->GetLength()) != 0)
{
ExitNow(rval = true);
}
if (hasRoute && (memcmp(hasRoute, FindHasRoute(*prefixBase), hasRoute->GetLength()) != 0))
{
ExitNow(rval = true);
}
}
}
cur = cur->GetNext();
}
exit:
(void)aRloc16;
return rval;
}
ThreadError Leader::RegisterNetworkData(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength)
{
ThreadError error = kThreadError_None;
bool rlocIn = false;
bool rlocStable = false;
bool stableUpdated = false;
RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength);
if (rlocIn)
{
if (IsStableUpdated(aRloc16, aTlvs, aTlvsLength, mTlvs, mLength) ||
IsStableUpdated(aRloc16, mTlvs, mLength, aTlvs, aTlvsLength))
{
stableUpdated = true;
}
SuccessOrExit(error = RemoveRloc(aRloc16));
SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength));
mVersion++;
if (stableUpdated)
{
mStableVersion++;
}
}
else
{
RlocLookup(aRloc16, rlocIn, rlocStable, aTlvs, aTlvsLength);
SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength));
mVersion++;
if (rlocStable)
{
mStableVersion++;
}
}
mNetif.SetStateChangedFlags(OT_THREAD_NETDATA_UPDATED);
exit:
return error;
}
ThreadError Leader::AddNetworkData(uint8_t *aTlvs, uint8_t aTlvsLength)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aTlvs + aTlvsLength);
while (cur < end)
{
switch (cur->GetType())
{
case NetworkDataTlv::kTypePrefix:
AddPrefix(*reinterpret_cast<PrefixTlv *>(cur));
otDumpDebgNetData("add prefix done", mTlvs, mLength);
break;
default:
assert(false);
break;
}
cur = cur->GetNext();
}
otDumpDebgNetData("add done", mTlvs, mLength);
return kThreadError_None;
}
ThreadError Leader::AddPrefix(PrefixTlv &aPrefix)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
while (cur < end)
{
switch (cur->GetType())
{
case NetworkDataTlv::kTypeHasRoute:
AddHasRoute(aPrefix, *reinterpret_cast<HasRouteTlv *>(cur));
break;
case NetworkDataTlv::kTypeBorderRouter:
AddBorderRouter(aPrefix, *reinterpret_cast<BorderRouterTlv *>(cur));
break;
default:
assert(false);
break;
}
cur = cur->GetNext();
}
return kThreadError_None;
}
ThreadError Leader::AddHasRoute(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute)
{
ThreadError error = kThreadError_None;
PrefixTlv *dstPrefix;
HasRouteTlv *dstHasRoute;
if ((dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength())) == NULL)
{
dstPrefix = reinterpret_cast<PrefixTlv *>(mTlvs + mLength);
Insert(reinterpret_cast<uint8_t *>(dstPrefix), sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()));
dstPrefix->Init(aPrefix.GetDomainId(), aPrefix.GetPrefixLength(), aPrefix.GetPrefix());
}
if (aHasRoute.IsStable())
{
dstPrefix->SetStable();
}
if ((dstHasRoute = FindHasRoute(*dstPrefix, aHasRoute.IsStable())) == NULL)
{
dstHasRoute = reinterpret_cast<HasRouteTlv *>(dstPrefix->GetNext());
Insert(reinterpret_cast<uint8_t *>(dstHasRoute), sizeof(HasRouteTlv));
dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(HasRouteTlv));
dstHasRoute->Init();
if (aHasRoute.IsStable())
{
dstHasRoute->SetStable();
}
}
Insert(reinterpret_cast<uint8_t *>(dstHasRoute->GetNext()), sizeof(HasRouteEntry));
dstHasRoute->SetLength(dstHasRoute->GetLength() + sizeof(HasRouteEntry));
dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(HasRouteEntry));
memcpy(dstHasRoute->GetEntry(dstHasRoute->GetNumEntries() - 1), aHasRoute.GetEntry(0),
sizeof(HasRouteEntry));
return error;
}
ThreadError Leader::AddBorderRouter(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter)
{
ThreadError error = kThreadError_None;
PrefixTlv *dstPrefix;
ContextTlv *dstContext;
BorderRouterTlv *dstBorderRouter;
int contextId;
if ((dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength())) == NULL)
{
dstPrefix = reinterpret_cast<PrefixTlv *>(mTlvs + mLength);
Insert(reinterpret_cast<uint8_t *>(dstPrefix), sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()));
dstPrefix->Init(aPrefix.GetDomainId(), aPrefix.GetPrefixLength(), aPrefix.GetPrefix());
}
if ((dstContext = FindContext(*dstPrefix)) != NULL)
{
dstContext->SetCompress();
}
else if ((contextId = AllocateContext()) >= 0)
{
dstContext = reinterpret_cast<ContextTlv *>(dstPrefix->GetNext());
Insert(reinterpret_cast<uint8_t *>(dstContext), sizeof(ContextTlv));
dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(ContextTlv));
dstContext->Init();
dstContext->SetCompress();
dstContext->SetContextId(static_cast<uint8_t>(contextId));
dstContext->SetContextLength(aPrefix.GetPrefixLength());
}
VerifyOrExit(dstContext != NULL, error = kThreadError_NoBufs);
mContextLastUsed[dstContext->GetContextId() - kMinContextId] = 0;
if ((dstBorderRouter = FindBorderRouter(*dstPrefix, aBorderRouter.IsStable())) == NULL)
{
dstBorderRouter = reinterpret_cast<BorderRouterTlv *>(dstPrefix->GetNext());
Insert(reinterpret_cast<uint8_t *>(dstBorderRouter), sizeof(BorderRouterTlv));
dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(BorderRouterTlv));
dstBorderRouter->Init();
}
Insert(reinterpret_cast<uint8_t *>(dstBorderRouter->GetNext()), sizeof(BorderRouterEntry));
dstBorderRouter->SetLength(dstBorderRouter->GetLength() + sizeof(BorderRouterEntry));
dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(BorderRouterEntry));
memcpy(dstBorderRouter->GetEntry(dstBorderRouter->GetNumEntries() - 1), aBorderRouter.GetEntry(0),
sizeof(BorderRouterEntry));
if (aBorderRouter.IsStable())
{
dstPrefix->SetStable();
dstContext->SetStable();
dstBorderRouter->SetStable();
}
exit:
return error;
}
int Leader::AllocateContext(void)
{
int rval = -1;
for (int i = kMinContextId; i < kMinContextId + kNumContextIds; i++)
{
if ((mContextUsed & (1 << i)) == 0)
{
mContextUsed |= 1 << i;
rval = i;
otLogInfoNetData("Allocated Context ID = %d\n", rval);
ExitNow();
}
}
exit:
return rval;
}
ThreadError Leader::FreeContext(uint8_t aContextId)
{
otLogInfoNetData("Free Context Id = %d\n", aContextId);
RemoveContext(aContextId);
mContextUsed &= ~(1 << aContextId);
mVersion++;
mStableVersion++;
mNetif.SetStateChangedFlags(OT_THREAD_NETDATA_UPDATED);
return kThreadError_None;
}
ThreadError Leader::SendServerDataNotification(uint16_t aRloc16)
{
ThreadError error = kThreadError_None;
bool rlocIn = false;
bool rlocStable = false;
RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength);
VerifyOrExit(rlocIn, error = kThreadError_NotFound);
SuccessOrExit(error = NetworkData::SendServerDataNotification(false, aRloc16));
exit:
return error;
}
ThreadError Leader::RemoveRloc(uint16_t aRloc16)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
NetworkDataTlv *end;
PrefixTlv *prefix;
while (1)
{
end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
if (cur >= end)
{
break;
}
switch (cur->GetType())
{
case NetworkDataTlv::kTypePrefix:
{
prefix = reinterpret_cast<PrefixTlv *>(cur);
RemoveRloc(*prefix, aRloc16);
if (prefix->GetSubTlvsLength() == 0)
{
Remove(reinterpret_cast<uint8_t *>(prefix), sizeof(NetworkDataTlv) + prefix->GetLength());
continue;
}
otDumpDebgNetData("remove prefix done", mTlvs, mLength);
break;
}
default:
{
assert(false);
break;
}
}
cur = cur->GetNext();
}
otDumpDebgNetData("remove done", mTlvs, mLength);
return kThreadError_None;
}
ThreadError Leader::RemoveRloc(PrefixTlv &prefix, uint16_t aRloc16)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(prefix.GetSubTlvs());
NetworkDataTlv *end;
ContextTlv *context;
while (1)
{
end = reinterpret_cast<NetworkDataTlv *>(prefix.GetSubTlvs() + prefix.GetSubTlvsLength());
if (cur >= end)
{
break;
}
switch (cur->GetType())
{
case NetworkDataTlv::kTypeHasRoute:
RemoveRloc(prefix, *reinterpret_cast<HasRouteTlv *>(cur), aRloc16);
// remove has route tlv if empty
if (cur->GetLength() == 0)
{
prefix.SetSubTlvsLength(prefix.GetSubTlvsLength() - sizeof(HasRouteTlv));
Remove(reinterpret_cast<uint8_t *>(cur), sizeof(HasRouteTlv));
continue;
}
break;
case NetworkDataTlv::kTypeBorderRouter:
RemoveRloc(prefix, *reinterpret_cast<BorderRouterTlv *>(cur), aRloc16);
// remove border router tlv if empty
if (cur->GetLength() == 0)
{
prefix.SetSubTlvsLength(prefix.GetSubTlvsLength() - sizeof(BorderRouterTlv));
Remove(reinterpret_cast<uint8_t *>(cur), sizeof(BorderRouterTlv));
continue;
}
break;
case NetworkDataTlv::kTypeContext:
break;
default:
assert(false);
break;
}
cur = cur->GetNext();
}
if ((context = FindContext(prefix)) != NULL)
{
if (prefix.GetSubTlvsLength() == sizeof(ContextTlv))
{
context->ClearCompress();
mContextLastUsed[context->GetContextId() - kMinContextId] = Timer::GetNow();
if (mContextLastUsed[context->GetContextId() - kMinContextId] == 0)
{
mContextLastUsed[context->GetContextId() - kMinContextId] = 1;
}
mTimer.Start(kStateUpdatePeriod);
}
else
{
context->SetCompress();
mContextLastUsed[context->GetContextId() - kMinContextId] = 0;
}
}
return kThreadError_None;
}
ThreadError Leader::RemoveRloc(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute, uint16_t aRloc16)
{
HasRouteEntry *entry;
// remove rloc from has route tlv
for (uint8_t i = 0; i < aHasRoute.GetNumEntries(); i++)
{
entry = aHasRoute.GetEntry(i);
if (entry->GetRloc() != aRloc16)
{
continue;
}
aHasRoute.SetLength(aHasRoute.GetLength() - sizeof(HasRouteEntry));
aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(HasRouteEntry));
Remove(reinterpret_cast<uint8_t *>(entry), sizeof(*entry));
break;
}
return kThreadError_None;
}
ThreadError Leader::RemoveRloc(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter, uint16_t aRloc16)
{
BorderRouterEntry *entry;
// remove rloc from border router tlv
for (uint8_t i = 0; i < aBorderRouter.GetNumEntries(); i++)
{
entry = aBorderRouter.GetEntry(i);
if (entry->GetRloc() != aRloc16)
{
continue;
}
aBorderRouter.SetLength(aBorderRouter.GetLength() - sizeof(BorderRouterEntry));
aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(BorderRouterEntry));
Remove(reinterpret_cast<uint8_t *>(entry), sizeof(*entry));
break;
}
return kThreadError_None;
}
ThreadError Leader::RemoveContext(uint8_t aContextId)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
NetworkDataTlv *end;
PrefixTlv *prefix;
while (1)
{
end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
if (cur >= end)
{
break;
}
switch (cur->GetType())
{
case NetworkDataTlv::kTypePrefix:
{
prefix = reinterpret_cast<PrefixTlv *>(cur);
RemoveContext(*prefix, aContextId);
if (prefix->GetSubTlvsLength() == 0)
{
Remove(reinterpret_cast<uint8_t *>(prefix), sizeof(NetworkDataTlv) + prefix->GetLength());
continue;
}
otDumpDebgNetData("remove prefix done", mTlvs, mLength);
break;
}
default:
{
assert(false);
break;
}
}
cur = cur->GetNext();
}
otDumpDebgNetData("remove done", mTlvs, mLength);
return kThreadError_None;
}
ThreadError Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end;
ContextTlv *context;
uint8_t length;
while (1)
{
end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
if (cur >= end)
{
break;
}
switch (cur->GetType())
{
case NetworkDataTlv::kTypeBorderRouter:
{
break;
}
case NetworkDataTlv::kTypeContext:
{
// remove context tlv
context = reinterpret_cast<ContextTlv *>(cur);
if (context->GetContextId() == aContextId)
{
length = sizeof(NetworkDataTlv) + context->GetLength();
aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - length);
Remove(reinterpret_cast<uint8_t *>(context), length);
continue;
}
break;
}
default:
{
assert(false);
break;
}
}
cur = cur->GetNext();
}
return kThreadError_None;
}
void Leader::HandleTimer(void *aContext)
{
Leader *obj = reinterpret_cast<Leader *>(aContext);
obj->HandleTimer();
}
void Leader::HandleTimer(void)
{
bool contextsWaiting = false;
for (uint8_t i = 0; i < kNumContextIds; i++)
{
if (mContextLastUsed[i] == 0)
{
continue;
}
if ((Timer::GetNow() - mContextLastUsed[i]) >= Timer::SecToMsec(mContextIdReuseDelay))
{
FreeContext(kMinContextId + i);
}
else
{
contextsWaiting = true;
}
}
if (contextsWaiting)
{
mTimer.Start(kStateUpdatePeriod);
}
}
} // namespace NetworkData
} // namespace Thread