blob: 67d3c34d562173a02bfc70c2a3c7596ecee910c8 [file] [log] [blame]
/*
* Copyright (c) 2016, Nest Labs, Inc.
* 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 common methods for manipulating Thread Network Data.
*/
#include <coap/coap_header.hpp>
#include <common/code_utils.hpp>
#include <common/debug.hpp>
#include <common/logging.hpp>
#include <mac/mac_frame.hpp>
#include <platform/random.h>
#include <thread/network_data.hpp>
#include <thread/thread_netif.hpp>
#include <thread/thread_tlvs.hpp>
#include <thread/thread_uris.hpp>
namespace Thread {
namespace NetworkData {
NetworkData::NetworkData(ThreadNetif &aThreadNetif):
mMle(aThreadNetif.GetMle())
{
mLength = 0;
mCoapMessageId = 0;
}
void NetworkData::GetNetworkData(bool aStable, uint8_t *aData, uint8_t &aDataLength)
{
assert(aData != NULL);
memcpy(aData, mTlvs, mLength);
aDataLength = mLength;
if (aStable)
{
RemoveTemporaryData(aData, aDataLength);
}
}
ThreadError NetworkData::GetNextOnMeshPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig)
{
return GetNextOnMeshPrefix(aIterator, Mac::kShortAddrBroadcast, aConfig);
}
ThreadError NetworkData::GetNextOnMeshPrefix(otNetworkDataIterator *aIterator, uint16_t aRloc16,
otBorderRouterConfig *aConfig)
{
ThreadError error = kThreadError_NotFound;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs + *aIterator);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext())
{
PrefixTlv *prefix;
BorderRouterTlv *borderRouter;
BorderRouterEntry *borderRouterEntry = NULL;
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
if ((borderRouter = FindBorderRouter(*prefix)) == NULL)
{
continue;
}
for (uint8_t i = 0; i < borderRouter->GetNumEntries(); i++)
{
if (aRloc16 == Mac::kShortAddrBroadcast || borderRouter->GetEntry(i)->GetRloc() == aRloc16)
{
borderRouterEntry = borderRouter->GetEntry(i);
break;
}
}
if (borderRouterEntry == NULL)
{
continue;
}
memset(aConfig, 0, sizeof(*aConfig));
memcpy(&aConfig->mPrefix.mPrefix, prefix->GetPrefix(), BitVectorBytes(prefix->GetPrefixLength()));
aConfig->mPrefix.mLength = prefix->GetPrefixLength();
aConfig->mPreference = borderRouterEntry->GetPreference();
aConfig->mPreferred = borderRouterEntry->IsPreferred();
aConfig->mSlaac = borderRouterEntry->IsSlaac();
aConfig->mDhcp = borderRouterEntry->IsDhcp();
aConfig->mConfigure = borderRouterEntry->IsConfigure();
aConfig->mDefaultRoute = borderRouterEntry->IsDefaultRoute();
aConfig->mOnMesh = borderRouterEntry->IsOnMesh();
aConfig->mStable = cur->IsStable();
*aIterator = static_cast<otNetworkDataIterator>(reinterpret_cast<uint8_t *>(cur->GetNext()) - mTlvs);
ExitNow(error = kThreadError_None);
}
exit:
return error;
}
ThreadError NetworkData::GetNextExternalRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig)
{
return GetNextExternalRoute(aIterator, Mac::kShortAddrBroadcast, aConfig);
}
ThreadError NetworkData::GetNextExternalRoute(otNetworkDataIterator *aIterator, uint16_t aRloc16,
otExternalRouteConfig *aConfig)
{
ThreadError error = kThreadError_NotFound;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs + *aIterator);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext())
{
PrefixTlv *prefix;
HasRouteTlv *hasRoute;
HasRouteEntry *hasRouteEntry = NULL;
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
if ((hasRoute = FindHasRoute(*prefix)) == NULL)
{
continue;
}
for (uint8_t i = 0; i < hasRoute->GetNumEntries(); i++)
{
if (aRloc16 == Mac::kShortAddrBroadcast || hasRoute->GetEntry(i)->GetRloc() == aRloc16)
{
hasRouteEntry = hasRoute->GetEntry(i);
break;
}
}
if (hasRouteEntry == NULL)
{
continue;
}
memset(aConfig, 0, sizeof(*aConfig));
memcpy(&aConfig->mPrefix.mPrefix, prefix->GetPrefix(), BitVectorBytes(prefix->GetPrefixLength()));
aConfig->mPrefix.mLength = prefix->GetPrefixLength();
aConfig->mPreference = hasRouteEntry->GetPreference();
aConfig->mStable = cur->IsStable();
*aIterator = static_cast<otNetworkDataIterator>(reinterpret_cast<uint8_t *>(cur->GetNext()) - mTlvs);
ExitNow(error = kThreadError_None);
}
exit:
return error;
}
bool NetworkData::ContainsOnMeshPrefixes(NetworkData &aCompare, uint16_t aRloc16)
{
otNetworkDataIterator outerIterator = OT_NETWORK_DATA_ITERATOR_INIT;
otBorderRouterConfig outerConfig;
bool rval = true;
while (aCompare.GetNextOnMeshPrefix(&outerIterator, aRloc16, &outerConfig) == kThreadError_None)
{
otNetworkDataIterator innerIterator = OT_NETWORK_DATA_ITERATOR_INIT;
otBorderRouterConfig innerConfig;
ThreadError error;
while ((error = GetNextOnMeshPrefix(&innerIterator, aRloc16, &innerConfig)) == kThreadError_None)
{
if (memcmp(&outerConfig, &innerConfig, sizeof(outerConfig)) == 0)
{
break;
}
}
if (error != kThreadError_None)
{
ExitNow(rval = false);
}
}
exit:
return rval;
}
bool NetworkData::ContainsExternalRoutes(NetworkData &aCompare, uint16_t aRloc16)
{
otNetworkDataIterator outerIterator = OT_NETWORK_DATA_ITERATOR_INIT;
otExternalRouteConfig outerConfig;
bool rval = true;
while (aCompare.GetNextExternalRoute(&outerIterator, aRloc16, &outerConfig) == kThreadError_None)
{
otNetworkDataIterator innerIterator = OT_NETWORK_DATA_ITERATOR_INIT;
otExternalRouteConfig innerConfig;
ThreadError error;
while ((error = GetNextExternalRoute(&innerIterator, aRloc16, &innerConfig)) == kThreadError_None)
{
if (memcmp(&outerConfig, &innerConfig, sizeof(outerConfig)) == 0)
{
break;
}
}
if (error != kThreadError_None)
{
ExitNow(rval = false);
}
}
exit:
return rval;
}
void NetworkData::RemoveTemporaryData(uint8_t *aData, uint8_t &aDataLength)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aData);
NetworkDataTlv *end;
PrefixTlv *prefix;
uint8_t length;
uint8_t *dst;
uint8_t *src;
while (1)
{
end = reinterpret_cast<NetworkDataTlv *>(aData + aDataLength);
if (cur >= end)
{
break;
}
switch (cur->GetType())
{
case NetworkDataTlv::kTypePrefix:
{
prefix = reinterpret_cast<PrefixTlv *>(cur);
RemoveTemporaryData(aData, aDataLength, *prefix);
if (prefix->GetSubTlvsLength() == 0)
{
length = sizeof(NetworkDataTlv) + cur->GetLength();
dst = reinterpret_cast<uint8_t *>(cur);
src = reinterpret_cast<uint8_t *>(cur->GetNext());
memmove(dst, src, aDataLength - static_cast<size_t>(src - aData));
aDataLength -= length;
continue;
}
otDumpDebgNetData("remove prefix done", mTlvs, mLength);
break;
}
default:
{
// remove temporary tlv
if (!cur->IsStable())
{
length = sizeof(NetworkDataTlv) + cur->GetLength();
dst = reinterpret_cast<uint8_t *>(cur);
src = reinterpret_cast<uint8_t *>(cur->GetNext());
memmove(dst, src, aDataLength - static_cast<size_t>(src - aData));
aDataLength -= length;
continue;
}
break;
}
}
cur = cur->GetNext();
}
otDumpDebgNetData("remove done", aData, aDataLength);
}
void NetworkData::RemoveTemporaryData(uint8_t *aData, uint8_t &aDataLength, PrefixTlv &aPrefix)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end;
BorderRouterTlv *borderRouter;
HasRouteTlv *hasRoute;
ContextTlv *context;
BorderRouterEntry *borderRouterEntry;
HasRouteEntry *hasRouteEntry;
uint8_t length;
uint8_t contextId;
uint8_t *dst;
uint8_t *src;
while (1)
{
end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
if (cur >= end)
{
break;
}
if (cur->IsStable())
{
switch (cur->GetType())
{
case NetworkDataTlv::kTypeBorderRouter:
{
borderRouter = FindBorderRouter(aPrefix);
if ((context = FindContext(aPrefix)) == NULL)
{
break;
}
contextId = context->GetContextId();
// replace p_border_router_16
for (uint8_t i = 0; i < borderRouter->GetNumEntries(); i++)
{
borderRouterEntry = borderRouter->GetEntry(i);
if (borderRouterEntry->IsDhcp() || borderRouterEntry->IsConfigure())
{
borderRouterEntry->SetRloc(0xfc00 | contextId);
}
else
{
borderRouterEntry->SetRloc(0xfffe);
}
}
break;
}
case NetworkDataTlv::kTypeHasRoute:
{
hasRoute = FindHasRoute(aPrefix);
// replace r_border_router_16
for (uint8_t j = 0; j < hasRoute->GetNumEntries(); j++)
{
hasRouteEntry = hasRoute->GetEntry(j);
hasRouteEntry->SetRloc(0xfffe);
}
break;
}
default:
{
break;
}
}
// keep stable tlv
cur = cur->GetNext();
}
else
{
// remove temporary tlv
length = sizeof(NetworkDataTlv) + cur->GetLength();
dst = reinterpret_cast<uint8_t *>(cur);
src = reinterpret_cast<uint8_t *>(cur->GetNext());
memmove(dst, src, aDataLength - static_cast<size_t>(src - aData));
aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - length);
aDataLength -= length;
}
}
}
BorderRouterTlv *NetworkData::FindBorderRouter(PrefixTlv &aPrefix)
{
BorderRouterTlv *rval = NULL;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypeBorderRouter)
{
ExitNow(rval = reinterpret_cast<BorderRouterTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
BorderRouterTlv *NetworkData::FindBorderRouter(PrefixTlv &aPrefix, bool aStable)
{
BorderRouterTlv *rval = NULL;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypeBorderRouter &&
cur->IsStable() == aStable)
{
ExitNow(rval = reinterpret_cast<BorderRouterTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
HasRouteTlv *NetworkData::FindHasRoute(PrefixTlv &aPrefix)
{
HasRouteTlv *rval = NULL;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypeHasRoute)
{
ExitNow(rval = reinterpret_cast<HasRouteTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
HasRouteTlv *NetworkData::FindHasRoute(PrefixTlv &aPrefix, bool aStable)
{
HasRouteTlv *rval = NULL;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypeHasRoute &&
cur->IsStable() == aStable)
{
ExitNow(rval = reinterpret_cast<HasRouteTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
ContextTlv *NetworkData::FindContext(PrefixTlv &aPrefix)
{
ContextTlv *rval = NULL;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs());
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aPrefix.GetSubTlvs() + aPrefix.GetSubTlvsLength());
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypeContext)
{
ExitNow(rval = reinterpret_cast<ContextTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
PrefixTlv *NetworkData::FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength)
{
return FindPrefix(aPrefix, aPrefixLength, mTlvs, mLength);
}
PrefixTlv *NetworkData::FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength, uint8_t *aTlvs, uint8_t aTlvsLength)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aTlvs + aTlvsLength);
PrefixTlv *compare;
while (cur < end)
{
if (cur->GetType() == NetworkDataTlv::kTypePrefix)
{
compare = reinterpret_cast<PrefixTlv *>(cur);
if (compare->GetPrefixLength() == aPrefixLength &&
PrefixMatch(compare->GetPrefix(), aPrefix, aPrefixLength) >= aPrefixLength)
{
return compare;
}
}
cur = cur->GetNext();
}
return NULL;
}
int8_t NetworkData::PrefixMatch(const uint8_t *a, const uint8_t *b, uint8_t aLength)
{
int8_t rval = 0;
uint8_t bytes = BitVectorBytes(aLength);
uint8_t diff;
for (uint8_t i = 0; i < bytes; i++)
{
diff = a[i] ^ b[i];
if (diff == 0)
{
rval += 8;
}
else
{
while ((diff & 0x80) == 0)
{
rval++;
diff <<= 1;
}
break;
}
}
return (rval >= aLength) ? rval : -1;
}
ThreadError NetworkData::Insert(uint8_t *aStart, uint8_t aLength)
{
assert(aLength + mLength <= sizeof(mTlvs) &&
mTlvs <= aStart &&
aStart <= mTlvs + mLength);
memmove(aStart + aLength, aStart, mLength - static_cast<size_t>(aStart - mTlvs));
mLength += aLength;
return kThreadError_None;
}
ThreadError NetworkData::Remove(uint8_t *aStart, uint8_t aLength)
{
assert(aLength <= mLength &&
mTlvs <= aStart &&
(aStart - mTlvs) + aLength <= mLength);
memmove(aStart, aStart + aLength, mLength - (static_cast<size_t>(aStart - mTlvs) + aLength));
mLength -= aLength;
return kThreadError_None;
}
ThreadError NetworkData::SendServerDataNotification(bool aLocal, uint16_t aRloc16)
{
ThreadError error = kThreadError_None;
Coap::Header header;
Message *message;
Ip6::MessageInfo messageInfo;
mSocket.Open(&HandleUdpReceive, this);
for (size_t i = 0; i < sizeof(mCoapToken); i++)
{
mCoapToken[i] = static_cast<uint8_t>(otPlatRandomGet());
}
header.Init();
header.SetVersion(1);
header.SetType(Coap::Header::kTypeConfirmable);
header.SetCode(Coap::Header::kCodePost);
header.SetMessageId(++mCoapMessageId);
header.SetToken(mCoapToken, sizeof(mCoapToken));
header.AppendUriPathOptions(OPENTHREAD_URI_SERVER_DATA);
header.AppendContentFormatOption(Coap::Header::kApplicationOctetStream);
header.Finalize();
VerifyOrExit((message = Ip6::Udp::NewMessage(0)) != NULL, error = kThreadError_NoBufs);
SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength()));
if (aLocal)
{
ThreadTlv tlv;
tlv.SetType(ThreadTlv::kThreadNetworkData);
tlv.SetLength(mLength);
SuccessOrExit(error = message->Append(&tlv, sizeof(tlv)));
SuccessOrExit(error = message->Append(mTlvs, mLength));
}
if (aRloc16 != Mac::kShortAddrInvalid)
{
ThreadRloc16Tlv rloc16Tlv;
rloc16Tlv.Init();
rloc16Tlv.SetRloc16(aRloc16);
SuccessOrExit(error = message->Append(&rloc16Tlv, sizeof(rloc16Tlv)));
}
memset(&messageInfo, 0, sizeof(messageInfo));
mMle.GetLeaderAddress(messageInfo.GetPeerAddr());
messageInfo.mPeerPort = kCoapUdpPort;
SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
otLogInfoNetData("Sent server data notification\n");
exit:
if (error != kThreadError_None && message != NULL)
{
Message::Free(*message);
}
return error;
}
void NetworkData::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo)
{
Local *obj = static_cast<Local *>(aContext);
obj->HandleUdpReceive(*static_cast<Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void NetworkData::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
{
Coap::Header header;
SuccessOrExit(header.FromMessage(aMessage));
VerifyOrExit(header.GetType() == Coap::Header::kTypeAcknowledgment &&
header.GetCode() == Coap::Header::kCodeChanged &&
header.GetMessageId() == mCoapMessageId &&
header.GetTokenLength() == sizeof(mCoapToken) &&
memcmp(mCoapToken, header.GetToken(), sizeof(mCoapToken)) == 0, ;);
otLogInfoNetData("Server data notification acknowledged\n");
exit:
(void)aMessageInfo;
}
} // namespace NetworkData
} // namespace Thread