| /* |
| * 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. |
| */ |
| |
| #if OPENTHREAD_FTD |
| |
| #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/timer.hpp" |
| #include "mac/mac_types.hpp" |
| #include "meshcop/meshcop.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" |
| |
| using ot::Encoding::BigEndian::HostSwap16; |
| |
| namespace ot { |
| namespace NetworkData { |
| |
| Leader::Leader(Instance &aInstance) |
| : LeaderBase(aInstance) |
| , mTimer(aInstance, &Leader::HandleTimer, this) |
| , mServerData(OT_URI_PATH_SERVER_DATA, &Leader::HandleServerData, this) |
| , mCommissioningDataGet(OT_URI_PATH_COMMISSIONER_GET, &Leader::HandleCommissioningGet, this) |
| , mCommissioningDataSet(OT_URI_PATH_COMMISSIONER_SET, &Leader::HandleCommissioningSet, this) |
| { |
| Reset(); |
| } |
| |
| void Leader::Reset(void) |
| { |
| LeaderBase::Reset(); |
| |
| memset(mContextLastUsed, 0, sizeof(mContextLastUsed)); |
| mContextUsed = 0; |
| mContextIdReuseDelay = kContextIdReuseDelay; |
| } |
| |
| void Leader::Start(void) |
| { |
| Get<Coap::Coap>().AddResource(mServerData); |
| Get<Coap::Coap>().AddResource(mCommissioningDataGet); |
| Get<Coap::Coap>().AddResource(mCommissioningDataSet); |
| } |
| |
| void Leader::Stop(void) |
| { |
| Get<Coap::Coap>().RemoveResource(mServerData); |
| Get<Coap::Coap>().RemoveResource(mCommissioningDataGet); |
| Get<Coap::Coap>().RemoveResource(mCommissioningDataSet); |
| } |
| |
| void Leader::IncrementVersion(void) |
| { |
| if (Get<Mle::MleRouter>().GetRole() == OT_DEVICE_ROLE_LEADER) |
| { |
| mVersion++; |
| Get<Notifier>().Signal(OT_CHANGED_THREAD_NETDATA); |
| } |
| } |
| |
| void Leader::IncrementStableVersion(void) |
| { |
| if (Get<Mle::MleRouter>().GetRole() == OT_DEVICE_ROLE_LEADER) |
| { |
| mStableVersion++; |
| } |
| } |
| |
| uint32_t Leader::GetContextIdReuseDelay(void) const |
| { |
| return mContextIdReuseDelay; |
| } |
| |
| void Leader::SetContextIdReuseDelay(uint32_t aDelay) |
| { |
| mContextIdReuseDelay = aDelay; |
| } |
| |
| void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode) |
| { |
| bool rlocIn = false; |
| bool rlocStable = false; |
| |
| RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength, aMatchMode); |
| VerifyOrExit(rlocIn); |
| RemoveRloc(aRloc16, aMatchMode); |
| |
| mVersion++; |
| |
| if (rlocStable) |
| { |
| mStableVersion++; |
| } |
| |
| Get<Notifier>().Signal(OT_CHANGED_THREAD_NETDATA); |
| |
| exit: |
| return; |
| } |
| |
| void Leader::HandleServerData(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Leader *>(aContext)->HandleServerData(*static_cast<Coap::Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Leader::HandleServerData(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| ThreadNetworkDataTlv networkData; |
| ThreadRloc16Tlv rloc16; |
| |
| otLogInfoNetData("Received network data registration"); |
| |
| if (ThreadTlv::GetTlv(aMessage, ThreadTlv::kRloc16, sizeof(rloc16), rloc16) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(rloc16.IsValid()); |
| RemoveBorderRouter(rloc16.GetRloc16(), kMatchModeRloc16); |
| } |
| |
| if (ThreadTlv::GetTlv(aMessage, ThreadTlv::kThreadNetworkData, sizeof(networkData), networkData) == OT_ERROR_NONE) |
| { |
| VerifyOrExit(networkData.IsValid()); |
| RegisterNetworkData(HostSwap16(aMessageInfo.mPeerAddr.mFields.m16[7]), networkData.GetTlvs(), |
| networkData.GetLength()); |
| } |
| |
| SuccessOrExit(Get<Coap::Coap>().SendEmptyAck(aMessage, aMessageInfo)); |
| |
| otLogInfoNetData("Sent network data registration acknowledgment"); |
| |
| exit: |
| return; |
| } |
| |
| void Leader::HandleCommissioningSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Leader *>(aContext)->HandleCommissioningSet(*static_cast<Coap::Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Leader::HandleCommissioningSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| uint16_t offset = aMessage.GetOffset(); |
| uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); |
| uint8_t tlvs[NetworkData::kMaxSize]; |
| MeshCoP::StateTlv::State state = MeshCoP::StateTlv::kReject; |
| bool hasSessionId = false; |
| bool hasValidTlv = false; |
| uint16_t sessionId = 0; |
| |
| MeshCoP::Tlv *cur; |
| MeshCoP::Tlv *end; |
| |
| VerifyOrExit(length <= sizeof(tlvs)); |
| VerifyOrExit(Get<Mle::MleRouter>().GetRole() == OT_DEVICE_ROLE_LEADER); |
| |
| aMessage.Read(offset, length, tlvs); |
| |
| // Session Id and Border Router Locator MUST NOT be set, but accept including unexpected or |
| // unknown TLV as long as there is at least one valid TLV. |
| cur = reinterpret_cast<MeshCoP::Tlv *>(tlvs); |
| end = reinterpret_cast<MeshCoP::Tlv *>(tlvs + length); |
| |
| while (cur < end) |
| { |
| MeshCoP::Tlv::Type type; |
| |
| VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end); |
| |
| type = cur->GetType(); |
| |
| if (type == MeshCoP::Tlv::kJoinerUdpPort || type == MeshCoP::Tlv::kSteeringData) |
| { |
| hasValidTlv = true; |
| } |
| else if (type == MeshCoP::Tlv::kBorderAgentLocator) |
| { |
| ExitNow(); |
| } |
| else if (type == MeshCoP::Tlv::kCommissionerSessionId) |
| { |
| MeshCoP::CommissionerSessionIdTlv *tlv = static_cast<MeshCoP::CommissionerSessionIdTlv *>(cur); |
| |
| VerifyOrExit(tlv->IsValid()); |
| sessionId = tlv->GetCommissionerSessionId(); |
| hasSessionId = true; |
| } |
| else |
| { |
| // do nothing for unexpected or unknown TLV |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| // verify whether or not commissioner session id TLV is included |
| VerifyOrExit(hasSessionId); |
| |
| // verify whether or not MGMT_COMM_SET.req includes at least one valid TLV |
| VerifyOrExit(hasValidTlv); |
| |
| // Find Commissioning Data TLV |
| for (NetworkDataTlv *netDataTlv = reinterpret_cast<NetworkDataTlv *>(mTlvs); |
| netDataTlv < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength); netDataTlv = netDataTlv->GetNext()) |
| { |
| if (netDataTlv->GetType() == NetworkDataTlv::kTypeCommissioningData) |
| { |
| // Iterate over MeshCoP TLVs and extract desired data |
| for (cur = reinterpret_cast<MeshCoP::Tlv *>(netDataTlv->GetValue()); |
| cur < reinterpret_cast<MeshCoP::Tlv *>(netDataTlv->GetValue() + netDataTlv->GetLength()); |
| cur = cur->GetNext()) |
| { |
| if (cur->GetType() == MeshCoP::Tlv::kCommissionerSessionId) |
| { |
| VerifyOrExit(sessionId == |
| static_cast<MeshCoP::CommissionerSessionIdTlv *>(cur)->GetCommissionerSessionId()); |
| } |
| else if (cur->GetType() == MeshCoP::Tlv::kBorderAgentLocator) |
| { |
| VerifyOrExit(length + cur->GetSize() <= sizeof(tlvs)); |
| memcpy(tlvs + length, reinterpret_cast<uint8_t *>(cur), cur->GetSize()); |
| length += cur->GetSize(); |
| } |
| } |
| } |
| } |
| |
| SetCommissioningData(tlvs, static_cast<uint8_t>(length)); |
| |
| state = MeshCoP::StateTlv::kAccept; |
| |
| exit: |
| |
| if (Get<Mle::MleRouter>().GetRole() == OT_DEVICE_ROLE_LEADER) |
| { |
| SendCommissioningSetResponse(aMessage, aMessageInfo, state); |
| } |
| } |
| |
| void Leader::HandleCommissioningGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Leader *>(aContext)->HandleCommissioningGet(*static_cast<Coap::Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Leader::HandleCommissioningGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| uint16_t length = 0; |
| uint16_t offset; |
| |
| SuccessOrExit(Tlv::GetValueOffset(aMessage, MeshCoP::Tlv::kGet, offset, length)); |
| aMessage.SetOffset(offset); |
| |
| exit: |
| SendCommissioningGetResponse(aMessage, length, aMessageInfo); |
| } |
| |
| void Leader::SendCommissioningGetResponse(const Coap::Message & aRequest, |
| uint16_t aLength, |
| const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Coap::Message *message; |
| uint8_t index; |
| uint8_t * data = NULL; |
| uint8_t length = 0; |
| |
| VerifyOrExit((message = MeshCoP::NewMeshCoPMessage(Get<Coap::Coap>())) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest)); |
| SuccessOrExit(error = message->SetPayloadMarker()); |
| |
| for (NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs); |
| cur < reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength); cur = cur->GetNext()) |
| { |
| if (cur->GetType() == NetworkDataTlv::kTypeCommissioningData) |
| { |
| data = cur->GetValue(); |
| length = cur->GetLength(); |
| break; |
| } |
| } |
| |
| VerifyOrExit(data && length, error = OT_ERROR_DROP); |
| |
| if (aLength == 0) |
| { |
| SuccessOrExit(error = message->Append(data, length)); |
| } |
| else |
| { |
| for (index = 0; index < aLength; index++) |
| { |
| uint8_t type; |
| |
| aRequest.Read(aRequest.GetOffset() + index, sizeof(type), &type); |
| |
| for (MeshCoP::Tlv *cur = reinterpret_cast<MeshCoP::Tlv *>(data); |
| cur < reinterpret_cast<MeshCoP::Tlv *>(data + length); cur = cur->GetNext()) |
| { |
| if (cur->GetType() == type) |
| { |
| SuccessOrExit(error = cur->AppendTo(*message)); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (message->GetLength() == message->GetOffset()) |
| { |
| // no payload, remove coap payload marker |
| message->SetLength(message->GetLength() - 1); |
| } |
| |
| SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, aMessageInfo)); |
| |
| otLogInfoMeshCoP("sent commissioning dataset get response"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| } |
| |
| void Leader::SendCommissioningSetResponse(const Coap::Message & aRequest, |
| const Ip6::MessageInfo & aMessageInfo, |
| MeshCoP::StateTlv::State aState) |
| { |
| otError error = OT_ERROR_NONE; |
| Coap::Message * message; |
| MeshCoP::StateTlv state; |
| |
| VerifyOrExit((message = MeshCoP::NewMeshCoPMessage(Get<Coap::Coap>())) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest)); |
| SuccessOrExit(error = message->SetPayloadMarker()); |
| |
| state.Init(); |
| state.SetState(aState); |
| SuccessOrExit(error = state.AppendTo(*message)); |
| |
| SuccessOrExit(error = Get<Coap::Coap>().SendMessage(*message, aMessageInfo)); |
| |
| otLogInfoMeshCoP("sent commissioning dataset set response"); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| } |
| |
| bool Leader::RlocMatch(uint16_t aFirstRloc16, uint16_t aSecondRloc16, MatchMode aMatchMode) |
| { |
| bool matched = false; |
| |
| switch (aMatchMode) |
| { |
| case kMatchModeRloc16: |
| matched = (aFirstRloc16 == aSecondRloc16); |
| break; |
| |
| case kMatchModeRouterId: |
| matched = Mle::Mle::RouterIdMatch(aFirstRloc16, aSecondRloc16); |
| break; |
| } |
| |
| return matched; |
| } |
| |
| otError Leader::RlocLookup(uint16_t aRloc16, |
| bool & aIn, |
| bool & aStable, |
| uint8_t * aTlvs, |
| uint8_t aTlvsLength, |
| MatchMode aMatchMode, |
| bool aAllowOtherEntries) |
| { |
| otError error = OT_ERROR_NONE; |
| 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; |
| ServiceTlv * service; |
| ServerTlv * server; |
| |
| while (cur < end) |
| { |
| VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypePrefix: |
| { |
| prefix = static_cast<PrefixTlv *>(cur); |
| VerifyOrExit(prefix->IsValid(), error = OT_ERROR_PARSE); |
| |
| subCur = prefix->GetSubTlvs(); |
| subEnd = prefix->GetNext(); |
| |
| VerifyOrExit(subEnd <= end, error = OT_ERROR_PARSE); |
| |
| while (subCur < subEnd) |
| { |
| VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE); |
| |
| switch (subCur->GetType()) |
| { |
| case NetworkDataTlv::kTypeBorderRouter: |
| borderRouter = static_cast<BorderRouterTlv *>(subCur); |
| |
| for (uint8_t i = 0; i < borderRouter->GetNumEntries(); i++) |
| { |
| borderRouterEntry = borderRouter->GetEntry(i); |
| |
| if (RlocMatch(borderRouterEntry->GetRloc(), aRloc16, aMatchMode)) |
| { |
| aIn = true; |
| |
| if (borderRouter->IsStable()) |
| { |
| aStable = true; |
| } |
| } |
| else |
| { |
| VerifyOrExit(aAllowOtherEntries, error = OT_ERROR_FAILED); |
| } |
| } |
| |
| break; |
| |
| case NetworkDataTlv::kTypeHasRoute: |
| hasRoute = static_cast<HasRouteTlv *>(subCur); |
| |
| for (uint8_t i = 0; i < hasRoute->GetNumEntries(); i++) |
| { |
| hasRouteEntry = hasRoute->GetEntry(i); |
| |
| if (RlocMatch(hasRouteEntry->GetRloc(), aRloc16, aMatchMode)) |
| { |
| aIn = true; |
| |
| if (hasRoute->IsStable()) |
| { |
| aStable = true; |
| } |
| } |
| else |
| { |
| VerifyOrExit(aAllowOtherEntries, error = OT_ERROR_FAILED); |
| } |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (aIn && aStable && aAllowOtherEntries) |
| { |
| ExitNow(); |
| } |
| |
| subCur = subCur->GetNext(); |
| } |
| } |
| break; |
| |
| case NetworkDataTlv::kTypeService: |
| { |
| service = static_cast<ServiceTlv *>(cur); |
| VerifyOrExit(service->IsValid(), error = OT_ERROR_PARSE); |
| |
| subCur = service->GetSubTlvs(); |
| subEnd = service->GetNext(); |
| |
| VerifyOrExit(subEnd <= end, error = OT_ERROR_PARSE); |
| |
| while (subCur < subEnd) |
| { |
| VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE); |
| |
| switch (subCur->GetType()) |
| { |
| case NetworkDataTlv::kTypeServer: |
| server = static_cast<ServerTlv *>(subCur); |
| VerifyOrExit(server->IsValid(), error = OT_ERROR_PARSE); |
| |
| if (RlocMatch(server->GetServer16(), aRloc16, aMatchMode)) |
| { |
| aIn = true; |
| |
| if (server->IsStable()) |
| { |
| aStable = true; |
| } |
| } |
| else |
| { |
| VerifyOrExit(aAllowOtherEntries, error = OT_ERROR_FAILED); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (aIn && aStable && aAllowOtherEntries) |
| { |
| ExitNow(); |
| } |
| |
| subCur = subCur->GetNext(); |
| } |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| bool Leader::IsStableUpdated(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); |
| ServiceTlv * service; |
| |
| while (cur < end) |
| { |
| VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end); |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypePrefix: |
| { |
| PrefixTlv * prefix = static_cast<PrefixTlv *>(cur); |
| ContextTlv * context = FindContext(*prefix); |
| BorderRouterTlv *borderRouter = FindBorderRouter(*prefix, true); |
| HasRouteTlv * hasRoute = FindHasRoute(*prefix, true); |
| |
| if (cur->IsStable() && (!context || borderRouter)) |
| { |
| PrefixTlv *prefixBase = |
| FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength(), aTlvsBase, aTlvsBaseLength); |
| |
| if (!prefixBase) |
| { |
| ExitNow(rval = true); |
| } |
| |
| if (borderRouter) |
| { |
| BorderRouterTlv *borderRouterBase = FindBorderRouter(*prefixBase, true); |
| |
| if (!borderRouterBase || (borderRouter->GetLength() != borderRouterBase->GetLength()) || |
| (memcmp(borderRouter, borderRouterBase, borderRouter->GetLength()) != 0)) |
| { |
| ExitNow(rval = true); |
| } |
| } |
| |
| if (hasRoute) |
| { |
| HasRouteTlv *hasRouteBase = FindHasRoute(*prefixBase, true); |
| |
| if (!hasRouteBase || (hasRoute->GetLength() != hasRouteBase->GetLength()) || |
| (memcmp(hasRoute, hasRouteBase, hasRoute->GetLength()) != 0)) |
| { |
| ExitNow(rval = true); |
| } |
| } |
| } |
| |
| break; |
| } |
| |
| case NetworkDataTlv::kTypeService: |
| service = static_cast<ServiceTlv *>(cur); |
| |
| if (cur->IsStable()) |
| { |
| NetworkDataTlv *curInner; |
| NetworkDataTlv *endInner; |
| |
| ServiceTlv *serviceBase = FindService(service->GetEnterpriseNumber(), service->GetServiceData(), |
| service->GetServiceDataLength(), aTlvsBase, aTlvsBaseLength); |
| |
| if (!serviceBase || !serviceBase->IsStable()) |
| { |
| ExitNow(rval = true); |
| } |
| |
| curInner = service->GetSubTlvs(); |
| endInner = service->GetNext(); |
| |
| while (curInner < endInner) |
| { |
| if (curInner->IsStable()) |
| { |
| switch (curInner->GetType()) |
| { |
| case NetworkDataTlv::kTypeServer: |
| { |
| bool foundInBase = false; |
| ServerTlv *server = static_cast<ServerTlv *>(curInner); |
| |
| NetworkDataTlv *curServerBase = serviceBase->GetSubTlvs(); |
| NetworkDataTlv *endServerBase = serviceBase->GetNext(); |
| |
| while (curServerBase <= endServerBase) |
| { |
| ServerTlv *serverBase = static_cast<ServerTlv *>(curServerBase); |
| |
| VerifyOrExit((curServerBase + 1) <= endServerBase && curServerBase->GetNext() <= end); |
| |
| if (curServerBase->IsStable() && (server->GetServer16() == serverBase->GetServer16()) && |
| (server->GetServerDataLength() == serverBase->GetServerDataLength()) && |
| (memcmp(server->GetServerData(), serverBase->GetServerData(), |
| server->GetServerDataLength()) == 0)) |
| { |
| foundInBase = true; |
| break; |
| } |
| |
| curServerBase = curServerBase->GetNext(); |
| } |
| |
| if (!foundInBase) |
| { |
| ExitNow(rval = true); |
| } |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| curInner = curInner->GetNext(); |
| } |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| otError Leader::RegisterNetworkData(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength) |
| { |
| otError error = OT_ERROR_NONE; |
| bool rlocIn = false; |
| bool rlocStable = false; |
| bool stableUpdated = false; |
| bool unused; |
| uint8_t oldTlvs[NetworkData::kMaxSize]; |
| uint8_t oldTlvsLength = NetworkData::kMaxSize; |
| |
| // Verify that `aTlvs` only contains entries matching `aRloc16`. |
| SuccessOrExit(error = RlocLookup(aRloc16, rlocIn, rlocStable, aTlvs, aTlvsLength, kMatchModeRloc16, |
| /* aAllowOtherEntries */ false)); |
| |
| RlocLookup(aRloc16, rlocIn, unused, mTlvs, mLength, kMatchModeRloc16); |
| |
| if (rlocIn) |
| { |
| if (IsStableUpdated(aTlvs, aTlvsLength, mTlvs, mLength) || IsStableUpdated(mTlvs, mLength, aTlvs, aTlvsLength)) |
| { |
| stableUpdated = true; |
| } |
| |
| // Store old Service IDs for given rloc16, so updates to server will reuse the same Service ID |
| SuccessOrExit(error = GetNetworkData(false, oldTlvs, oldTlvsLength)); |
| |
| RemoveRloc(aRloc16, kMatchModeRloc16); |
| SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength, oldTlvs, oldTlvsLength)); |
| |
| mVersion++; |
| |
| if (stableUpdated) |
| { |
| mStableVersion++; |
| } |
| } |
| else |
| { |
| // No old data to be preserved, lets avoid memcpy() & FindService calls. |
| SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength, oldTlvs, 0)); |
| |
| mVersion++; |
| |
| if (rlocStable) |
| { |
| mStableVersion++; |
| } |
| } |
| |
| Get<Notifier>().Signal(OT_CHANGED_THREAD_NETDATA); |
| |
| exit: |
| return error; |
| } |
| |
| otError Leader::AddNetworkData(uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aOldTlvs, uint8_t aOldTlvsLength) |
| { |
| otError error = OT_ERROR_NONE; |
| NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(aTlvs); |
| NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(aTlvs + aTlvsLength); |
| |
| while (cur < end) |
| { |
| VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypePrefix: |
| SuccessOrExit(error = AddPrefix(*static_cast<PrefixTlv *>(cur))); |
| otDumpDebgNetData("add prefix done", mTlvs, mLength); |
| break; |
| |
| case NetworkDataTlv::kTypeService: |
| SuccessOrExit(error = AddService(*static_cast<ServiceTlv *>(cur), aOldTlvs, aOldTlvsLength)); |
| otDumpDebgNetData("add service done", mTlvs, mLength); |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| otDumpDebgNetData("add done", mTlvs, mLength); |
| |
| exit: |
| return error; |
| } |
| |
| otError Leader::AddPrefix(PrefixTlv &aPrefix) |
| { |
| otError error = OT_ERROR_NONE; |
| NetworkDataTlv *cur; |
| NetworkDataTlv *end; |
| |
| VerifyOrExit(aPrefix.IsValid(), error = OT_ERROR_PARSE); |
| cur = aPrefix.GetSubTlvs(); |
| end = aPrefix.GetNext(); |
| |
| while (cur < end) |
| { |
| VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypeHasRoute: |
| SuccessOrExit(error = AddHasRoute(aPrefix, *static_cast<HasRouteTlv *>(cur))); |
| break; |
| |
| case NetworkDataTlv::kTypeBorderRouter: |
| SuccessOrExit(error = AddBorderRouter(aPrefix, *static_cast<BorderRouterTlv *>(cur))); |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Leader::AddService(ServiceTlv &aService, uint8_t *aOldTlvs, uint8_t aOldTlvsLength) |
| { |
| otError error = OT_ERROR_NONE; |
| NetworkDataTlv *cur; |
| NetworkDataTlv *end; |
| |
| VerifyOrExit(aService.IsValid(), error = OT_ERROR_PARSE); |
| cur = aService.GetSubTlvs(); |
| end = aService.GetNext(); |
| |
| while (cur < end) |
| { |
| VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypeServer: |
| SuccessOrExit(error = AddServer(aService, *static_cast<ServerTlv *>(cur), aOldTlvs, aOldTlvsLength)); |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError Leader::AddHasRoute(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute) |
| { |
| otError error = OT_ERROR_NONE; |
| PrefixTlv * dstPrefix = NULL; |
| HasRouteTlv *dstHasRoute = NULL; |
| uint16_t appendLength = 0; |
| |
| VerifyOrExit(aHasRoute.GetNumEntries() > 0, error = OT_ERROR_PARSE); |
| |
| if ((dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength())) != NULL) |
| { |
| dstHasRoute = FindHasRoute(*dstPrefix, aHasRoute.IsStable()); |
| } |
| |
| if (dstPrefix == NULL) |
| { |
| appendLength += sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()); |
| } |
| |
| if (dstHasRoute == NULL) |
| { |
| appendLength += sizeof(HasRouteTlv); |
| } |
| |
| appendLength += sizeof(HasRouteEntry); |
| |
| VerifyOrExit(mLength + appendLength <= sizeof(mTlvs), error = OT_ERROR_NO_BUFS); |
| |
| if (dstPrefix == 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 == NULL) |
| { |
| dstHasRoute = static_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)); |
| |
| exit: |
| return error; |
| } |
| |
| otError Leader::AddServer(ServiceTlv &aService, ServerTlv &aServer, uint8_t *aOldTlvs, uint8_t aOldTlvsLength) |
| { |
| otError error = OT_ERROR_NONE; |
| ServiceTlv *dstService = NULL; |
| ServiceTlv *oldService = NULL; |
| ServerTlv * dstServer = NULL; |
| uint16_t appendLength = 0; |
| uint8_t serviceID = 0; |
| uint8_t serviceInsertLength = sizeof(ServiceTlv) + sizeof(uint8_t) /*mServiceDataLength*/ + |
| ServiceTlv::GetEnterpriseNumberFieldLength(aService.GetEnterpriseNumber()) + |
| aService.GetServiceDataLength(); |
| |
| dstService = |
| FindService(aService.GetEnterpriseNumber(), aService.GetServiceData(), aService.GetServiceDataLength()); |
| |
| if (dstService == NULL) |
| { |
| appendLength += serviceInsertLength; |
| } |
| |
| appendLength += sizeof(ServerTlv) + aServer.GetServerDataLength(); |
| |
| VerifyOrExit(mLength + appendLength <= sizeof(mTlvs), error = OT_ERROR_NO_BUFS); |
| |
| if (dstService == NULL) |
| { |
| // Try to preserve old Service ID, if existing |
| oldService = FindService(aService.GetEnterpriseNumber(), aService.GetServiceData(), |
| aService.GetServiceDataLength(), aOldTlvs, aOldTlvsLength); |
| |
| if (oldService != NULL) |
| { |
| // The same service is not found in current data, but was in old data. So, it had to be just removed by |
| // RemoveRloc() Lets use the same ServiceID |
| serviceID = oldService->GetServiceID(); |
| } |
| else |
| { |
| uint8_t i; |
| |
| // This seems like completely new service. Lets try to find new ServiceID for it. If all are taken, error |
| // out. Since we call FindServiceById() on mTlv, we need to execute this before Insert() call, otherwise |
| // we'll find uninitialized service as well. |
| for (i = Mle::kServiceMinId; i <= Mle::kServiceMaxId; i++) |
| { |
| if (FindServiceById(i) == NULL) |
| { |
| serviceID = i; |
| break; |
| } |
| } |
| |
| otLogInfoNetData("Allocated Service ID = %d", i); |
| |
| VerifyOrExit(i <= Mle::kServiceMaxId, error = OT_ERROR_NO_BUFS); |
| } |
| |
| dstService = reinterpret_cast<ServiceTlv *>(mTlvs + mLength); |
| Insert(reinterpret_cast<uint8_t *>(dstService), serviceInsertLength); |
| dstService->Init(); |
| dstService->SetServiceID(serviceID); |
| dstService->SetEnterpriseNumber(aService.GetEnterpriseNumber()); |
| dstService->SetServiceData(aService.GetServiceData(), aService.GetServiceDataLength()); |
| dstService->SetLength(serviceInsertLength - sizeof(NetworkDataTlv)); |
| } |
| |
| dstServer = static_cast<ServerTlv *>(dstService->GetNext()); |
| |
| Insert(reinterpret_cast<uint8_t *>(dstServer), sizeof(ServerTlv) + aServer.GetServerDataLength()); |
| dstServer->Init(); |
| dstServer->SetServer16(aServer.GetServer16()); |
| dstServer->SetServerData(aServer.GetServerData(), aServer.GetServerDataLength()); |
| |
| if (aServer.IsStable()) |
| { |
| dstService->SetStable(); |
| dstServer->SetStable(); |
| } |
| |
| dstService->SetLength(dstService->GetLength() + sizeof(ServerTlv) + aServer.GetServerDataLength()); |
| |
| exit: |
| return error; |
| } |
| |
| ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) |
| { |
| NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs); |
| NetworkDataTlv *end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength); |
| 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->GetServiceID() == aServiceId) |
| { |
| ExitNow(); |
| } |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| compare = NULL; |
| |
| exit: |
| return compare; |
| } |
| |
| otError Leader::AddBorderRouter(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter) |
| { |
| otError error = OT_ERROR_NONE; |
| PrefixTlv * dstPrefix = NULL; |
| ContextTlv * dstContext = NULL; |
| BorderRouterTlv *dstBorderRouter = NULL; |
| int contextId = -1; |
| uint16_t appendLength = 0; |
| |
| VerifyOrExit(aBorderRouter.GetNumEntries() > 0, error = OT_ERROR_PARSE); |
| |
| if ((dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength())) != NULL) |
| { |
| dstContext = FindContext(*dstPrefix); |
| dstBorderRouter = FindBorderRouter(*dstPrefix, aBorderRouter.IsStable()); |
| } |
| |
| if (dstPrefix == NULL) |
| { |
| appendLength += sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()); |
| } |
| |
| if (dstContext == NULL) |
| { |
| appendLength += sizeof(ContextTlv); |
| } |
| |
| if (dstBorderRouter == NULL) |
| { |
| appendLength += sizeof(BorderRouterTlv); |
| } |
| |
| appendLength += sizeof(BorderRouterEntry); |
| |
| VerifyOrExit(mLength + appendLength <= sizeof(mTlvs), error = OT_ERROR_NO_BUFS); |
| |
| if (dstContext == NULL) |
| { |
| contextId = AllocateContext(); |
| VerifyOrExit(contextId >= 0, error = OT_ERROR_NO_BUFS); |
| } |
| |
| if (dstPrefix == 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 == NULL) |
| { |
| dstContext = static_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()); |
| } |
| |
| dstContext->SetCompress(); |
| StopContextReuseTimer(dstContext->GetContextId()); |
| |
| if (dstBorderRouter == NULL) |
| { |
| dstBorderRouter = static_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", rval); |
| ExitNow(); |
| } |
| } |
| |
| exit: |
| return rval; |
| } |
| |
| void Leader::FreeContext(uint8_t aContextId) |
| { |
| otLogInfoNetData("Free Context Id = %d", aContextId); |
| RemoveContext(aContextId); |
| mContextUsed &= ~(1 << aContextId); |
| mVersion++; |
| mStableVersion++; |
| Get<Notifier>().Signal(OT_CHANGED_THREAD_NETDATA); |
| } |
| |
| void Leader::StartContextReuseTimer(uint8_t aContextId) |
| { |
| mContextLastUsed[aContextId - kMinContextId] = TimerMilli::GetNow(); |
| |
| if (mContextLastUsed[aContextId - kMinContextId].GetValue() == 0) |
| { |
| mContextLastUsed[aContextId - kMinContextId].SetValue(1); |
| } |
| |
| mTimer.Start(kStateUpdatePeriod); |
| } |
| |
| void Leader::StopContextReuseTimer(uint8_t aContextId) |
| { |
| mContextLastUsed[aContextId - kMinContextId].SetValue(0); |
| } |
| |
| otError Leader::SendServerDataNotification(uint16_t aRloc16) |
| { |
| otError error = OT_ERROR_NONE; |
| bool rlocIn = false; |
| bool rlocStable = false; |
| |
| RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength, kMatchModeRloc16); |
| |
| VerifyOrExit(rlocIn, error = OT_ERROR_NOT_FOUND); |
| |
| SuccessOrExit(error = NetworkData::SendServerDataNotification(aRloc16)); |
| |
| exit: |
| return error; |
| } |
| |
| void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode) |
| { |
| NetworkDataTlv *cur = reinterpret_cast<NetworkDataTlv *>(mTlvs); |
| NetworkDataTlv *end; |
| PrefixTlv * prefix; |
| ServiceTlv * service; |
| |
| while (1) |
| { |
| end = reinterpret_cast<NetworkDataTlv *>(mTlvs + mLength); |
| |
| if (cur >= end) |
| { |
| break; |
| } |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypePrefix: |
| { |
| prefix = static_cast<PrefixTlv *>(cur); |
| RemoveRloc(*prefix, aRloc16, aMatchMode); |
| |
| if (prefix->GetSubTlvsLength() == 0) |
| { |
| Remove(reinterpret_cast<uint8_t *>(prefix), sizeof(NetworkDataTlv) + prefix->GetLength()); |
| continue; |
| } |
| |
| otDumpDebgNetData("remove prefix done", mTlvs, mLength); |
| break; |
| } |
| |
| case NetworkDataTlv::kTypeService: |
| { |
| service = static_cast<ServiceTlv *>(cur); |
| RemoveRloc(*service, aRloc16, aMatchMode); |
| |
| if (service->GetSubTlvsLength() == 0) |
| { |
| Remove(reinterpret_cast<uint8_t *>(service), sizeof(NetworkDataTlv) + service->GetLength()); |
| continue; |
| } |
| |
| otDumpDebgNetData("remove service done", mTlvs, mLength); |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| otDumpDebgNetData("remove done", mTlvs, mLength); |
| } |
| |
| void Leader::RemoveRloc(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMode) |
| { |
| NetworkDataTlv *cur = aPrefix.GetSubTlvs(); |
| NetworkDataTlv *end; |
| ContextTlv * context; |
| |
| while (1) |
| { |
| end = aPrefix.GetNext(); |
| |
| if (cur >= end) |
| { |
| break; |
| } |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypeHasRoute: |
| RemoveRloc(aPrefix, *static_cast<HasRouteTlv *>(cur), aRloc16, aMatchMode); |
| |
| // remove has route tlv if empty |
| if (cur->GetLength() == 0) |
| { |
| aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(HasRouteTlv)); |
| Remove(reinterpret_cast<uint8_t *>(cur), sizeof(HasRouteTlv)); |
| continue; |
| } |
| |
| break; |
| |
| case NetworkDataTlv::kTypeBorderRouter: |
| RemoveRloc(aPrefix, *static_cast<BorderRouterTlv *>(cur), aRloc16, aMatchMode); |
| |
| // remove border router tlv if empty |
| if (cur->GetLength() == 0) |
| { |
| aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(BorderRouterTlv)); |
| Remove(reinterpret_cast<uint8_t *>(cur), sizeof(BorderRouterTlv)); |
| continue; |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| if ((context = FindContext(aPrefix)) != NULL) |
| { |
| if (aPrefix.GetSubTlvsLength() == sizeof(ContextTlv)) |
| { |
| context->ClearCompress(); |
| StartContextReuseTimer(context->GetContextId()); |
| } |
| else |
| { |
| context->SetCompress(); |
| StopContextReuseTimer(context->GetContextId()); |
| } |
| } |
| } |
| |
| void Leader::RemoveRloc(ServiceTlv &aService, uint16_t aRloc16, MatchMode aMatchMode) |
| { |
| NetworkDataTlv *cur = aService.GetSubTlvs(); |
| NetworkDataTlv *end; |
| ServerTlv * server; |
| uint8_t removeLength; |
| |
| while (1) |
| { |
| end = aService.GetNext(); |
| |
| if (cur >= end) |
| { |
| break; |
| } |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypeServer: |
| server = static_cast<ServerTlv *>(cur); |
| |
| if (RlocMatch(server->GetServer16(), aRloc16, aMatchMode)) |
| { |
| removeLength = sizeof(ServerTlv) + server->GetServerDataLength(); |
| aService.SetSubTlvsLength(aService.GetSubTlvsLength() - removeLength); |
| Remove(reinterpret_cast<uint8_t *>(cur), removeLength); |
| continue; |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| } |
| |
| void Leader::RemoveRloc(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute, uint16_t aRloc16, MatchMode aMatchMode) |
| { |
| HasRouteEntry *entry = aHasRoute.GetFirstEntry(); |
| |
| while (entry <= aHasRoute.GetLastEntry()) |
| { |
| if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode)) |
| { |
| aHasRoute.SetLength(aHasRoute.GetLength() - sizeof(HasRouteEntry)); |
| aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(HasRouteEntry)); |
| Remove(reinterpret_cast<uint8_t *>(entry), sizeof(HasRouteEntry)); |
| continue; |
| } |
| |
| entry = entry->GetNext(); |
| } |
| } |
| |
| void Leader::RemoveRloc(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter, uint16_t aRloc16, MatchMode aMatchMode) |
| { |
| BorderRouterEntry *entry = aBorderRouter.GetFirstEntry(); |
| |
| while (entry <= aBorderRouter.GetLastEntry()) |
| { |
| if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode)) |
| { |
| aBorderRouter.SetLength(aBorderRouter.GetLength() - sizeof(BorderRouterEntry)); |
| aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(BorderRouterEntry)); |
| Remove(reinterpret_cast<uint8_t *>(entry), sizeof(*entry)); |
| continue; |
| } |
| |
| entry = entry->GetNext(); |
| } |
| } |
| |
| void 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 = static_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: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| otDumpDebgNetData("remove done", mTlvs, mLength); |
| } |
| |
| void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId) |
| { |
| NetworkDataTlv *cur = aPrefix.GetSubTlvs(); |
| NetworkDataTlv *end; |
| ContextTlv * context; |
| uint8_t length; |
| |
| while (1) |
| { |
| end = aPrefix.GetNext(); |
| |
| if (cur >= end) |
| { |
| break; |
| } |
| |
| switch (cur->GetType()) |
| { |
| case NetworkDataTlv::kTypeContext: |
| { |
| // remove context tlv |
| context = static_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: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| } |
| |
| void Leader::UpdateContextsAfterReset(void) |
| { |
| PrefixTlv * prefix; |
| ContextTlv *contextTlv; |
| |
| // Iterate through Network Data and synchronize missing contexts. |
| 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; |
| } |
| |
| mContextUsed |= 1 << contextTlv->GetContextId(); |
| |
| if (contextTlv->IsCompress()) |
| { |
| StopContextReuseTimer(contextTlv->GetContextId()); |
| } |
| else |
| { |
| StartContextReuseTimer(contextTlv->GetContextId()); |
| } |
| } |
| } |
| |
| void Leader::HandleTimer(Timer &aTimer) |
| { |
| aTimer.GetOwner<Leader>().HandleTimer(); |
| } |
| |
| void Leader::HandleTimer(void) |
| { |
| bool contextsWaiting = false; |
| |
| for (uint8_t i = 0; i < kNumContextIds; i++) |
| { |
| if (mContextLastUsed[i].GetValue() == 0) |
| { |
| continue; |
| } |
| |
| if (TimerMilli::GetNow() - mContextLastUsed[i] >= Time::SecToMsec(mContextIdReuseDelay)) |
| { |
| FreeContext(kMinContextId + i); |
| } |
| else |
| { |
| contextsWaiting = true; |
| } |
| } |
| |
| if (contextsWaiting) |
| { |
| mTimer.Start(kStateUpdatePeriod); |
| } |
| } |
| |
| } // namespace NetworkData |
| } // namespace ot |
| |
| #endif // OPENTHREAD_FTD |