blob: 851e84b1dbda7b810d24957171232d4be293c024 [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 common methods for manipulating Thread Network Data.
*/
#include "network_data.hpp"
#include "coap/coap_message.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "mac/mac_types.hpp"
#include "thread/thread_netif.hpp"
#include "thread/thread_tlvs.hpp"
#include "thread/thread_uri_paths.hpp"
namespace ot {
namespace NetworkData {
NetworkData::NetworkData(Instance &aInstance, Type aType)
: InstanceLocator(aInstance)
, mType(aType)
, mLastAttemptWait(false)
, mLastAttempt(0)
{
mLength = 0;
}
void NetworkData::Clear(void)
{
mLength = 0;
}
otError NetworkData::GetNetworkData(bool aStable, uint8_t *aData, uint8_t &aDataLength)
{
otError error = OT_ERROR_NONE;
assert(aData != NULL);
VerifyOrExit(aDataLength >= mLength, error = OT_ERROR_NO_BUFS);
memcpy(aData, mTlvs, mLength);
aDataLength = mLength;
if (aStable)
{
RemoveTemporaryData(aData, aDataLength);
}
exit:
return error;
}
otError NetworkData::GetNextOnMeshPrefix(Iterator &aIterator, OnMeshPrefixConfig &aConfig)
{
return GetNextOnMeshPrefix(aIterator, Mac::kShortAddrBroadcast, aConfig);
}
otError NetworkData::GetNextOnMeshPrefix(Iterator &aIterator, uint16_t aRloc16, OnMeshPrefixConfig &aConfig)
{
otError error = OT_ERROR_NOT_FOUND;
NetworkDataIterator iterator(aIterator);
NetworkDataTlv * cur = reinterpret_cast<NetworkDataTlv *>(mTlvs + iterator.GetTlvOffset());
NetworkDataTlv * end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext(), iterator.SetSubTlvOffset(0), iterator.SetEntryIndex(0))
{
PrefixTlv * prefix;
NetworkDataTlv *subCur;
NetworkDataTlv *subEnd;
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE);
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
subCur = reinterpret_cast<NetworkDataTlv *>(reinterpret_cast<uint8_t *>(prefix->GetSubTlvs()) +
iterator.GetSubTlvOffset());
subEnd = cur->GetNext();
for (; subCur < subEnd; subCur = subCur->GetNext(), iterator.SetEntryIndex(0))
{
BorderRouterTlv *borderRouter;
VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE);
if (subCur->GetType() != NetworkDataTlv::kTypeBorderRouter)
{
continue;
}
borderRouter = static_cast<BorderRouterTlv *>(subCur);
for (uint8_t index = iterator.GetEntryIndex(); index < borderRouter->GetNumEntries(); index++)
{
if (aRloc16 == Mac::kShortAddrBroadcast || borderRouter->GetEntry(index)->GetRloc() == aRloc16)
{
BorderRouterEntry *borderRouterEntry = borderRouter->GetEntry(index);
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 = borderRouter->IsStable();
aConfig.mRloc16 = borderRouterEntry->GetRloc();
iterator.SaveTlvOffset(cur, mTlvs);
iterator.SaveSubTlvOffset(subCur, prefix->GetSubTlvs());
iterator.SetEntryIndex(index + 1);
ExitNow(error = OT_ERROR_NONE);
}
}
}
}
exit:
return error;
}
otError NetworkData::GetNextExternalRoute(Iterator &aIterator, ExternalRouteConfig &aConfig)
{
return GetNextExternalRoute(aIterator, Mac::kShortAddrBroadcast, aConfig);
}
otError NetworkData::GetNextExternalRoute(Iterator &aIterator, uint16_t aRloc16, ExternalRouteConfig &aConfig)
{
otError error = OT_ERROR_NOT_FOUND;
NetworkDataIterator iterator(aIterator);
NetworkDataTlv * cur = reinterpret_cast<NetworkDataTlv *>(mTlvs + iterator.GetTlvOffset());
NetworkDataTlv * end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext(), iterator.SetSubTlvOffset(0), iterator.SetEntryIndex(0))
{
PrefixTlv * prefix;
NetworkDataTlv *subCur;
NetworkDataTlv *subEnd;
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE);
if (cur->GetType() != NetworkDataTlv::kTypePrefix)
{
continue;
}
prefix = static_cast<PrefixTlv *>(cur);
subCur = reinterpret_cast<NetworkDataTlv *>(reinterpret_cast<uint8_t *>(prefix->GetSubTlvs()) +
iterator.GetSubTlvOffset());
subEnd = cur->GetNext();
for (; subCur < subEnd; subCur = subCur->GetNext(), iterator.SetEntryIndex(0))
{
HasRouteTlv *hasRoute;
VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE);
if (subCur->GetType() != NetworkDataTlv::kTypeHasRoute)
{
continue;
}
hasRoute = static_cast<HasRouteTlv *>(subCur);
for (uint8_t index = iterator.GetEntryIndex(); index < hasRoute->GetNumEntries(); index++)
{
if (aRloc16 == Mac::kShortAddrBroadcast || hasRoute->GetEntry(index)->GetRloc() == aRloc16)
{
HasRouteEntry *hasRouteEntry = hasRoute->GetEntry(index);
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 = hasRoute->IsStable();
aConfig.mRloc16 = hasRouteEntry->GetRloc();
aConfig.mNextHopIsThisDevice = (hasRouteEntry->GetRloc() == Get<Mle::MleRouter>().GetRloc16());
iterator.SaveTlvOffset(cur, mTlvs);
iterator.SaveSubTlvOffset(subCur, prefix->GetSubTlvs());
iterator.SetEntryIndex(index + 1);
ExitNow(error = OT_ERROR_NONE);
}
}
}
}
exit:
return error;
}
otError NetworkData::GetNextService(Iterator &aIterator, ServiceConfig &aConfig)
{
return GetNextService(aIterator, Mac::kShortAddrBroadcast, aConfig);
}
otError NetworkData::GetNextService(Iterator &aIterator, uint16_t aRloc16, ServiceConfig &aConfig)
{
otError error = OT_ERROR_NOT_FOUND;
NetworkDataIterator iterator(aIterator);
NetworkDataTlv * cur = reinterpret_cast<NetworkDataTlv *>(mTlvs + iterator.GetTlvOffset());
NetworkDataTlv * end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext(), iterator.SetSubTlvOffset(0))
{
ServiceTlv * service;
NetworkDataTlv *subCur;
NetworkDataTlv *subEnd;
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE);
if (cur->GetType() != NetworkDataTlv::kTypeService)
{
continue;
}
service = static_cast<ServiceTlv *>(cur);
subCur = reinterpret_cast<NetworkDataTlv *>(reinterpret_cast<uint8_t *>(service->GetSubTlvs()) +
iterator.GetSubTlvOffset());
subEnd = cur->GetNext();
for (; subCur < subEnd; subCur = subCur->GetNext())
{
ServerTlv *server;
VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE);
if (subCur->GetType() != NetworkDataTlv::kTypeServer)
{
continue;
}
server = static_cast<ServerTlv *>(subCur);
if ((aRloc16 == Mac::kShortAddrBroadcast) || (server->GetServer16() == aRloc16))
{
memset(&aConfig, 0, sizeof(aConfig));
aConfig.mServiceID = service->GetServiceID();
aConfig.mEnterpriseNumber = service->GetEnterpriseNumber();
aConfig.mServiceDataLength = service->GetServiceDataLength();
memcpy(&aConfig.mServiceData, service->GetServiceData(), service->GetServiceDataLength());
aConfig.mServerConfig.mStable = server->IsStable();
aConfig.mServerConfig.mServerDataLength = server->GetServerDataLength();
memcpy(&aConfig.mServerConfig.mServerData, server->GetServerData(), server->GetServerDataLength());
aConfig.mServerConfig.mRloc16 = server->GetServer16();
if (subCur->GetNext() >= cur->GetNext())
{
iterator.SaveTlvOffset(cur->GetNext(), mTlvs);
iterator.SetSubTlvOffset(0);
}
else
{
iterator.SaveTlvOffset(cur, mTlvs);
iterator.SaveSubTlvOffset(subCur->GetNext(), service->GetSubTlvs());
}
ExitNow(error = OT_ERROR_NONE);
}
}
}
exit:
return error;
}
otError NetworkData::GetNextServiceId(Iterator &aIterator, uint16_t aRloc16, uint8_t &aServiceId)
{
otError error = OT_ERROR_NOT_FOUND;
NetworkDataIterator iterator(aIterator);
NetworkDataTlv * cur = reinterpret_cast<NetworkDataTlv *>(mTlvs + iterator.GetTlvOffset());
NetworkDataTlv * end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext(), iterator.SetSubTlvOffset(0))
{
ServiceTlv * service;
NetworkDataTlv *subCur;
NetworkDataTlv *subEnd;
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE);
if (cur->GetType() != NetworkDataTlv::kTypeService)
{
continue;
}
service = static_cast<ServiceTlv *>(cur);
subCur = reinterpret_cast<NetworkDataTlv *>(reinterpret_cast<uint8_t *>(service->GetSubTlvs()) +
iterator.GetSubTlvOffset());
subEnd = cur->GetNext();
for (; subCur < subEnd; subCur = subCur->GetNext())
{
ServerTlv *server;
VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE);
if (subCur->GetType() != NetworkDataTlv::kTypeServer)
{
continue;
}
server = static_cast<ServerTlv *>(subCur);
if ((aRloc16 == Mac::kShortAddrBroadcast) || (server->GetServer16() == aRloc16))
{
aServiceId = service->GetServiceID();
if (subCur->GetNext() >= cur->GetNext())
{
iterator.SaveTlvOffset(cur->GetNext(), mTlvs);
iterator.SetSubTlvOffset(0);
}
else
{
iterator.SaveTlvOffset(cur, mTlvs);
iterator.SaveSubTlvOffset(subCur->GetNext(), service->GetSubTlvs());
}
ExitNow(error = OT_ERROR_NONE);
}
}
}
exit:
return error;
}
bool NetworkData::ContainsOnMeshPrefixes(NetworkData &aCompare, uint16_t aRloc16)
{
Iterator outerIterator = kIteratorInit;
OnMeshPrefixConfig outerConfig;
bool rval = true;
while (aCompare.GetNextOnMeshPrefix(outerIterator, aRloc16, outerConfig) == OT_ERROR_NONE)
{
Iterator innerIterator = kIteratorInit;
OnMeshPrefixConfig innerConfig;
otError error;
while ((error = GetNextOnMeshPrefix(innerIterator, aRloc16, innerConfig)) == OT_ERROR_NONE)
{
if (memcmp(&outerConfig, &innerConfig, sizeof(outerConfig)) == 0)
{
break;
}
}
if (error != OT_ERROR_NONE)
{
ExitNow(rval = false);
}
}
exit:
return rval;
}
bool NetworkData::ContainsExternalRoutes(NetworkData &aCompare, uint16_t aRloc16)
{
Iterator outerIterator = kIteratorInit;
ExternalRouteConfig outerConfig;
bool rval = true;
while (aCompare.GetNextExternalRoute(outerIterator, aRloc16, outerConfig) == OT_ERROR_NONE)
{
Iterator innerIterator = kIteratorInit;
ExternalRouteConfig innerConfig;
otError error;
while ((error = GetNextExternalRoute(innerIterator, aRloc16, innerConfig)) == OT_ERROR_NONE)
{
if (memcmp(&outerConfig, &innerConfig, sizeof(outerConfig)) == 0)
{
break;
}
}
if (error != OT_ERROR_NONE)
{
ExitNow(rval = false);
}
}
exit:
return rval;
}
bool NetworkData::ContainsServices(NetworkData &aCompare, uint16_t aRloc16)
{
Iterator outerIterator = kIteratorInit;
ServiceConfig outerConfig;
bool rval = true;
while (aCompare.GetNextService(outerIterator, aRloc16, outerConfig) == OT_ERROR_NONE)
{
Iterator innerIterator = kIteratorInit;
ServiceConfig innerConfig;
otError error;
while ((error = GetNextService(innerIterator, aRloc16, innerConfig)) == OT_ERROR_NONE)
{
if ((outerConfig.mEnterpriseNumber == innerConfig.mEnterpriseNumber) &&
(outerConfig.mServiceDataLength == innerConfig.mServiceDataLength) &&
(memcmp(outerConfig.mServiceData, innerConfig.mServiceData, outerConfig.mServiceDataLength) == 0) &&
(outerConfig.mServerConfig.mStable == innerConfig.mServerConfig.mStable) &&
(outerConfig.mServerConfig.mServerDataLength == innerConfig.mServerConfig.mServerDataLength) &&
(memcmp(outerConfig.mServerConfig.mServerData, innerConfig.mServerConfig.mServerData,
outerConfig.mServerConfig.mServerDataLength) == 0))
{
break;
}
}
if (error != OT_ERROR_NONE)
{
ExitNow(rval = false);
}
}
exit:
return rval;
}
bool NetworkData::ContainsService(uint8_t aServiceId, uint16_t aRloc16)
{
bool rval = false;
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength);
for (; cur < end; cur = cur->GetNext())
{
ServiceTlv * service;
NetworkDataTlv *subCur;
NetworkDataTlv *subEnd;
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() != NetworkDataTlv::kTypeService)
{
continue;
}
service = static_cast<ServiceTlv *>(cur);
if (service->GetServiceID() == aServiceId)
{
subCur = service->GetSubTlvs();
subEnd = cur->GetNext();
for (; subCur < subEnd; subCur = subCur->GetNext())
{
ServerTlv *server;
VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd);
if (subCur->GetType() != NetworkDataTlv::kTypeServer)
{
continue;
}
server = static_cast<ServerTlv *>(subCur);
if (server->GetServer16() == aRloc16)
{
ExitNow(rval = true);
}
}
}
}
exit:
return rval;
}
void NetworkData::RemoveTemporaryData(uint8_t *aData, uint8_t &aDataLength)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aData);
NetworkDataTlv *end;
PrefixTlv * prefix;
ServiceTlv * service;
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 = static_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;
}
case NetworkDataTlv::kTypeService:
{
service = static_cast<ServiceTlv *>(cur);
RemoveTemporaryData(aData, aDataLength, *service);
if (service->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 service 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 = 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 = aPrefix.GetNext();
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;
}
}
}
void NetworkData::RemoveTemporaryData(uint8_t *aData, uint8_t &aDataLength, ServiceTlv &aService)
{
NetworkDataTlv *cur = aService.GetSubTlvs();
NetworkDataTlv *end;
ServerTlv * server;
uint8_t length;
uint8_t * dst;
uint8_t * src;
while (1)
{
end = aService.GetNext();
if (cur >= end)
{
break;
}
if (cur->IsStable())
{
switch (cur->GetType())
{
case NetworkDataTlv::kTypeServer:
{
server = static_cast<ServerTlv *>(cur);
server->SetServer16(Mle::Mle::ServiceAlocFromId(aService.GetServiceID()));
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));
aService.SetSubTlvsLength(aService.GetSubTlvsLength() - length);
aDataLength -= length;
}
}
}
BorderRouterTlv *NetworkData::FindBorderRouter(PrefixTlv &aPrefix)
{
BorderRouterTlv *rval = NULL;
NetworkDataTlv * cur = aPrefix.GetSubTlvs();
NetworkDataTlv * end = aPrefix.GetNext();
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypeBorderRouter)
{
ExitNow(rval = static_cast<BorderRouterTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
BorderRouterTlv *NetworkData::FindBorderRouter(PrefixTlv &aPrefix, bool aStable)
{
BorderRouterTlv *rval = NULL;
NetworkDataTlv * cur = aPrefix.GetSubTlvs();
NetworkDataTlv * end = aPrefix.GetNext();
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypeBorderRouter && cur->IsStable() == aStable)
{
ExitNow(rval = static_cast<BorderRouterTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
HasRouteTlv *NetworkData::FindHasRoute(PrefixTlv &aPrefix)
{
HasRouteTlv * rval = NULL;
NetworkDataTlv *cur = aPrefix.GetSubTlvs();
NetworkDataTlv *end = aPrefix.GetNext();
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypeHasRoute)
{
ExitNow(rval = static_cast<HasRouteTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
HasRouteTlv *NetworkData::FindHasRoute(PrefixTlv &aPrefix, bool aStable)
{
HasRouteTlv * rval = NULL;
NetworkDataTlv *cur = aPrefix.GetSubTlvs();
NetworkDataTlv *end = aPrefix.GetNext();
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypeHasRoute && cur->IsStable() == aStable)
{
ExitNow(rval = static_cast<HasRouteTlv *>(cur));
}
cur = cur->GetNext();
}
exit:
return rval;
}
ContextTlv *NetworkData::FindContext(PrefixTlv &aPrefix)
{
ContextTlv * rval = NULL;
NetworkDataTlv *cur = aPrefix.GetSubTlvs();
NetworkDataTlv *end = aPrefix.GetNext();
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypeContext)
{
ExitNow(rval = static_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 = NULL;
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypePrefix)
{
compare = static_cast<PrefixTlv *>(cur);
if (compare->GetPrefixLength() == aPrefixLength &&
PrefixMatch(compare->GetPrefix(), aPrefix, aPrefixLength) >= aPrefixLength)
{
ExitNow();
}
}
cur = cur->GetNext();
}
compare = NULL;
exit:
return compare;
}
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;
}
ServiceTlv *NetworkData::FindService(uint32_t aEnterpriseNumber,
const uint8_t *aServiceData,
uint8_t aServiceDataLength)
{
return FindService(aEnterpriseNumber, aServiceData, aServiceDataLength, mTlvs, mLength);
}
ServiceTlv *NetworkData::FindService(uint32_t aEnterpriseNumber,
const uint8_t *aServiceData,
uint8_t aServiceDataLength,
uint8_t * aTlvs,
uint8_t aTlvsLength)
{
NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aTlvs);
NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aTlvs + aTlvsLength);
ServiceTlv * compare = NULL;
while (cur < end)
{
VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
if (cur->GetType() == NetworkDataTlv::kTypeService)
{
compare = static_cast<ServiceTlv *>(cur);
if ((compare->GetEnterpriseNumber() == aEnterpriseNumber) &&
(compare->GetServiceDataLength() == aServiceDataLength) &&
(memcmp(compare->GetServiceData(), aServiceData, aServiceDataLength) == 0))
{
ExitNow();
}
}
cur = cur->GetNext();
}
compare = NULL;
exit:
return compare;
}
void 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;
}
void 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;
}
otError NetworkData::SendServerDataNotification(uint16_t aRloc16)
{
otError error = OT_ERROR_NONE;
Coap::Message * message = NULL;
Ip6::MessageInfo messageInfo;
VerifyOrExit(!mLastAttemptWait || (TimerMilli::GetNow() - mLastAttempt < kDataResubmitDelay),
error = OT_ERROR_ALREADY);
VerifyOrExit((message = Get<Coap::Coap>().NewMessage()) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = message->Init(OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST, OT_URI_PATH_SERVER_DATA));
SuccessOrExit(error = message->SetPayloadMarker());
if (mType == kTypeLocal)
{
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 = rloc16Tlv.AppendTo(*message));
}
Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr());
messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
messageInfo.SetPeerPort(kCoapUdpPort);
SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, messageInfo));
if (mType == kTypeLocal)
{
mLastAttempt = TimerMilli::GetNow();
mLastAttemptWait = true;
}
otLogInfoNetData("Sent server data notification");
exit:
if (error != OT_ERROR_NONE && message != NULL)
{
message->Free();
}
return error;
}
void NetworkData::ClearResubmitDelayTimer(void)
{
mLastAttemptWait = false;
}
} // namespace NetworkData
} // namespace ot