diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index bfe774a37..544d75414 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -240,7 +240,7 @@ enum AlocAllocation * Service IDs * */ -enum ServiceId +enum { kServiceMinId = 0x00, ///< Minimal Service ID. kServiceMaxId = 0x0f, ///< Maximal Service ID. diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index 60b2b11f1..543a3583a 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -712,14 +712,14 @@ exit: return serviceTlv; } -NetworkDataTlv *NetworkData::AppendTlv(uint8_t aTlvSize) +NetworkDataTlv *NetworkData::AppendTlv(uint16_t aTlvSize) { NetworkDataTlv *tlv; - VerifyOrExit(mLength + aTlvSize <= kMaxSize, tlv = NULL); + VerifyOrExit(CanInsert(aTlvSize), tlv = NULL); tlv = GetTlvsEnd(); - mLength += aTlvSize; + mLength += static_cast(aTlvSize); exit: return tlv; @@ -729,7 +729,7 @@ void NetworkData::Insert(void *aStart, uint8_t aLength) { uint8_t *start = reinterpret_cast(aStart); - OT_ASSERT(aLength + mLength <= sizeof(mTlvs) && mTlvs <= start && start <= mTlvs + mLength); + OT_ASSERT(CanInsert(aLength) && mTlvs <= start && start <= mTlvs + mLength); memmove(start + aLength, start, mLength - static_cast(start - mTlvs)); mLength += aLength; } diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp index 0a6125d45..ee39ed410 100644 --- a/src/core/thread/network_data.hpp +++ b/src/core/thread/network_data.hpp @@ -596,6 +596,18 @@ protected: const uint8_t *aTlvs, uint8_t aTlvsLength); + /** + * This method indicates whether there is space in Network Data to insert/append new info and grow it by a given + * number of bytes. + * + * @param[in] aSize The number of bytes to grow the Network Data. + * + * @retval TRUE There is space to grow Network Data by @p aSize bytes. + * @retval FALSE There is no space left to grow Network Data by @p aSize bytes. + * + */ + bool CanInsert(uint16_t aSize) const { return (mLength + aSize <= kMaxSize); } + /** * This method grows the Network Data to append a TLV with a requested size. * @@ -608,7 +620,7 @@ protected: * Data with requested @p aTlvSize number of bytes. * */ - NetworkDataTlv *AppendTlv(uint8_t aTlvSize); + NetworkDataTlv *AppendTlv(uint16_t aTlvSize); /** * This method inserts bytes into the Network Data. diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index 00e17e502..16d999d59 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -104,6 +104,14 @@ void Leader::IncrementVersionAndStableVersion(void) } } +void Leader::IncrementVersions(const ChangedFlags &aFlags) +{ + if (aFlags.DidChange()) + { + IncrementVersions(aFlags.DidStableChange()); + } +} + void Leader::IncrementVersions(bool aIncludeStable) { if (aIncludeStable) @@ -117,16 +125,10 @@ void Leader::IncrementVersions(bool aIncludeStable) void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode) { - bool rlocIn; - bool rlocStable; + ChangedFlags flags; - RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength, aMatchMode); - VerifyOrExit(rlocIn); - RemoveRloc(aRloc16, aMatchMode); - IncrementVersions(rlocStable); - -exit: - return; + RemoveRloc(aRloc16, aMatchMode, flags); + IncrementVersions(flags); } void Leader::HandleServerData(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) @@ -402,20 +404,15 @@ bool Leader::RlocMatch(uint16_t aFirstRloc16, uint16_t aSecondRloc16, MatchMode return matched; } -otError Leader::RlocLookup(uint16_t aRloc16, - bool & aIn, - bool & aStable, - const uint8_t *aTlvs, - uint8_t aTlvsLength, - MatchMode aMatchMode, - bool aAllowOtherEntries) +otError Leader::Validate(const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aRloc16) { + // Validate that the `aTlvs` contains well-formed TLVs, sub-TLVs, + // and entries all matching `aRloc16` (no other entry for other + // RLOCs and no duplicates TLVs). + otError error = OT_ERROR_NONE; const NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); - aIn = false; - aStable = false; - for (const NetworkDataTlv *cur = reinterpret_cast(aTlvs); cur < end; cur = cur->GetNext()) { VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); @@ -424,196 +421,15 @@ otError Leader::RlocLookup(uint16_t aRloc16, { case NetworkDataTlv::kTypePrefix: { - const PrefixTlv * prefix = static_cast(cur); - const NetworkDataTlv *subEnd; + const PrefixTlv *prefix = static_cast(cur); VerifyOrExit(prefix->IsValid(), error = OT_ERROR_PARSE); - subEnd = prefix->GetNext(); - - for (const NetworkDataTlv *subCur = prefix->GetSubTlvs(); subCur < subEnd; subCur = subCur->GetNext()) - { - VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE); - - switch (subCur->GetType()) - { - case NetworkDataTlv::kTypeBorderRouter: - { - const BorderRouterTlv *borderRouter = static_cast(subCur); - - for (const BorderRouterEntry *borderRouterEntry = borderRouter->GetFirstEntry(); - borderRouterEntry <= borderRouter->GetLastEntry(); - borderRouterEntry = borderRouterEntry->GetNext()) - { - if (RlocMatch(borderRouterEntry->GetRloc(), aRloc16, aMatchMode)) - { - aIn = true; - - if (borderRouter->IsStable()) - { - aStable = true; - } - } - else - { - VerifyOrExit(aAllowOtherEntries, error = OT_ERROR_FAILED); - } - } - - break; - } - - case NetworkDataTlv::kTypeHasRoute: - { - const HasRouteTlv *hasRoute = static_cast(subCur); - - for (const HasRouteEntry *hasRouteEntry = hasRoute->GetFirstEntry(); - hasRouteEntry <= hasRoute->GetLastEntry(); hasRouteEntry = hasRouteEntry->GetNext()) - { - 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(); - } - } - } - break; - - case NetworkDataTlv::kTypeService: - { - const ServiceTlv * service = static_cast(cur); - const NetworkDataTlv *subEnd; - - VerifyOrExit(service->IsValid(), error = OT_ERROR_PARSE); - - subEnd = service->GetNext(); - - for (const NetworkDataTlv *subCur = service->GetSubTlvs(); subCur < subEnd; subCur = subCur->GetNext()) - { - VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE); - - switch (subCur->GetType()) - { - case NetworkDataTlv::kTypeServer: - { - const ServerTlv *server = static_cast(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(); - } - } - - break; - } - - default: - break; - } - } - -exit: - return error; -} - -bool Leader::IsStableUpdated(const uint8_t *aTlvs, - uint8_t aTlvsLength, - const uint8_t *aTlvsBase, - uint8_t aTlvsBaseLength) -{ - bool rval = false; - const NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); - - for (const NetworkDataTlv *cur = reinterpret_cast(aTlvs); cur < end; cur = cur->GetNext()) - { - VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end); - - switch (cur->GetType()) - { - case NetworkDataTlv::kTypePrefix: - { - const PrefixTlv * prefix = static_cast(cur); - const ContextTlv * context = FindContext(*prefix); - const BorderRouterTlv *borderRouter = FindBorderRouter(*prefix, true); - const HasRouteTlv * hasRoute = FindHasRoute(*prefix, true); - - if (cur->IsStable() && (!context || borderRouter)) - { - const PrefixTlv *prefixBase = - FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength(), aTlvsBase, aTlvsBaseLength); - - if (!prefixBase) - { - ExitNow(rval = true); - } - - if (borderRouter) - { - const BorderRouterTlv *borderRouterBase = FindBorderRouter(*prefixBase, true); - - if (!borderRouterBase || (borderRouter->GetLength() != borderRouterBase->GetLength()) || - (memcmp(borderRouter, borderRouterBase, borderRouter->GetLength()) != 0)) - { - ExitNow(rval = true); - } - } - - if (hasRoute) - { - const HasRouteTlv *hasRouteBase = FindHasRoute(*prefixBase, true); - - if (!hasRouteBase || (hasRoute->GetLength() != hasRouteBase->GetLength()) || - (memcmp(hasRoute, hasRouteBase, hasRoute->GetLength()) != 0)) - { - ExitNow(rval = true); - } - } - } + // Ensure there is no duplicate Prefix TLVs with same prefix. + VerifyOrExit(prefix == FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength(), aTlvs, aTlvsLength), + error = OT_ERROR_PARSE); + SuccessOrExit(error = ValidatePrefix(*prefix, aRloc16)); break; } @@ -621,75 +437,15 @@ bool Leader::IsStableUpdated(const uint8_t *aTlvs, { const ServiceTlv *service = static_cast(cur); - if (cur->IsStable()) - { - const NetworkDataTlv *curInner; - const NetworkDataTlv *endInner; + VerifyOrExit(service->IsValid(), error = OT_ERROR_PARSE); - const 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) - { - VerifyOrExit((curInner + 1) <= endInner && curInner->GetNext() <= endInner); - - if (curInner->IsStable()) - { - switch (curInner->GetType()) - { - case NetworkDataTlv::kTypeServer: - { - bool foundInBase = false; - const ServerTlv *server = static_cast(curInner); - - const NetworkDataTlv *curServerBase = serviceBase->GetSubTlvs(); - const NetworkDataTlv *endServerBase = serviceBase->GetNext(); - - while (curServerBase < endServerBase) - { - const ServerTlv *serverBase = static_cast(curServerBase); - - VerifyOrExit((curServerBase + 1) <= endServerBase && - curServerBase->GetNext() <= endServerBase); - - 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(); - } - } + // Ensure there is no duplicate Service TLV with same + // Enterprise Number and Service Data. + VerifyOrExit(service == FindService(service->GetEnterpriseNumber(), service->GetServiceData(), + service->GetServiceDataLength(), aTlvs, aTlvsLength), + error = OT_ERROR_PARSE); + SuccessOrExit(error = ValidateService(*service, aRloc16)); break; } @@ -698,71 +454,269 @@ bool Leader::IsStableUpdated(const uint8_t *aTlvs, } } -exit: - return rval; -} - -otError Leader::RegisterNetworkData(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength) -{ - otError error = OT_ERROR_NONE; - bool rlocIn; - bool rlocStable; - bool unused; - uint8_t oldTlvs[NetworkData::kMaxSize]; - uint8_t oldTlvsLength = NetworkData::kMaxSize; - - VerifyOrExit(Get().IsAllocated(Mle::Mle::RouterIdFromRloc16(aRloc16)), error = OT_ERROR_NO_ROUTE); - - // 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)) - { - rlocStable = 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); - } - else - { - // No old data to be preserved, lets avoid memcpy() & FindService calls. - oldTlvsLength = 0; - } - - SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength, oldTlvs, oldTlvsLength)); - IncrementVersions(rlocStable); - exit: return error; } -otError Leader::AddNetworkData(uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aOldTlvs, uint8_t aOldTlvsLength) +otError Leader::ValidatePrefix(const PrefixTlv &aPrefix, uint16_t aRloc16) { - otError error = OT_ERROR_NONE; - NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); + // Validate that `aPrefix` TLV contains well-formed sub-TLVs and + // and entries all matching `aRloc16` (no other entry for other + // RLOCs). - for (NetworkDataTlv *cur = reinterpret_cast(aTlvs); cur < end; cur = cur->GetNext()) + otError error = OT_ERROR_NONE; + const NetworkDataTlv *subEnd = aPrefix.GetNext(); + bool foundTempHasRoute = false; + bool foundStableHasRoute = false; + bool foundTempBorderRouter = false; + bool foundStableBorderRouter = false; + + for (const NetworkDataTlv *subCur = aPrefix.GetSubTlvs(); subCur < subEnd; subCur = subCur->GetNext()) { - VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); + VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE); + switch (subCur->GetType()) + { + case NetworkDataTlv::kTypeBorderRouter: + { + const BorderRouterTlv *borderRouter = static_cast(subCur); + + // Ensure Prefix TLV contains at most one stable and one + // temporary Border Router sub-TLV and the sub-TLVs have + // a single entry. + + if (borderRouter->IsStable()) + { + VerifyOrExit(!foundStableBorderRouter, error = OT_ERROR_PARSE); + foundStableBorderRouter = true; + } + else + { + VerifyOrExit(!foundTempBorderRouter, error = OT_ERROR_PARSE); + foundTempBorderRouter = true; + } + + VerifyOrExit(borderRouter->GetFirstEntry() == borderRouter->GetLastEntry(), error = OT_ERROR_PARSE); + VerifyOrExit(borderRouter->GetFirstEntry()->GetRloc() == aRloc16, error = OT_ERROR_PARSE); + break; + } + + case NetworkDataTlv::kTypeHasRoute: + { + const HasRouteTlv *hasRoute = static_cast(subCur); + + // Ensure Prefix TLV contains at most one stable and one + // temporary Has Route sub-TLV and the sub-TLVs have a + // single entry. + + if (hasRoute->IsStable()) + { + VerifyOrExit(!foundStableHasRoute, error = OT_ERROR_PARSE); + foundStableHasRoute = true; + } + else + { + VerifyOrExit(!foundTempHasRoute, error = OT_ERROR_PARSE); + foundTempHasRoute = true; + } + + VerifyOrExit(hasRoute->GetFirstEntry() == hasRoute->GetLastEntry(), error = OT_ERROR_PARSE); + VerifyOrExit(hasRoute->GetFirstEntry()->GetRloc() == aRloc16, error = OT_ERROR_PARSE); + break; + } + + default: + break; + } + } + +exit: + return error; +} + +otError Leader::ValidateService(const ServiceTlv &aService, uint16_t aRloc16) +{ + // Validate that `aService` TLV contains a single well-formed + // Server sub-TLV associated with `aRloc16`. + + otError error = OT_ERROR_NONE; + const NetworkDataTlv *subEnd = aService.GetNext(); + bool foundServer = false; + + for (const NetworkDataTlv *subCur = aService.GetSubTlvs(); subCur < subEnd; subCur = subCur->GetNext()) + { + VerifyOrExit((subCur + 1) <= subEnd && subCur->GetNext() <= subEnd, error = OT_ERROR_PARSE); + + switch (subCur->GetType()) + { + case NetworkDataTlv::kTypeServer: + { + const ServerTlv *server = static_cast(subCur); + + VerifyOrExit(!foundServer, error = OT_ERROR_PARSE); + foundServer = true; + + VerifyOrExit(server->IsValid() && server->GetServer16() == aRloc16, error = OT_ERROR_PARSE); + break; + } + + default: + break; + } + } + +exit: + return error; +} + +bool Leader::ContainsMatchingEntry(const PrefixTlv *aPrefix, bool aStable, const HasRouteEntry &aEntry) +{ + // Check whether `aPrefix` has a Has Route sub-TLV with stable + // flag `aStable` containing a matching entry to `aEntry`. + + return (aPrefix == NULL) ? false : ContainsMatchingEntry(FindHasRoute(*aPrefix, aStable), aEntry); +} + +bool Leader::ContainsMatchingEntry(const HasRouteTlv *aHasRoute, const HasRouteEntry &aEntry) +{ + // Check whether `aHasRoute` has a matching entry to `aEntry`. + + bool contains = false; + + VerifyOrExit(aHasRoute != NULL); + + for (const HasRouteEntry *entry = aHasRoute->GetFirstEntry(); entry <= aHasRoute->GetLastEntry(); entry++) + { + if (*entry == aEntry) + { + contains = true; + break; + } + } + +exit: + return contains; +} + +bool Leader::ContainsMatchingEntry(const PrefixTlv *aPrefix, bool aStable, const BorderRouterEntry &aEntry) +{ + // Check whether `aPrefix` has a Border Router sub-TLV with stable + // flag `aStable` containing a matching entry to `aEntry`. + + return (aPrefix == NULL) ? false : ContainsMatchingEntry(FindBorderRouter(*aPrefix, aStable), aEntry); +} + +bool Leader::ContainsMatchingEntry(const BorderRouterTlv *aBorderRouter, const BorderRouterEntry &aEntry) +{ + // Check whether `aBorderRouter` has a matching entry to `aEntry`. + + bool contains = false; + + VerifyOrExit(aBorderRouter != NULL); + + for (const BorderRouterEntry *entry = aBorderRouter->GetFirstEntry(); entry <= aBorderRouter->GetLastEntry(); + entry++) + { + if (*entry == aEntry) + { + contains = true; + break; + } + } + +exit: + return contains; +} + +bool Leader::ContainsMatchingServer(const ServiceTlv *aService, const ServerTlv &aServer) +{ + // Check whether the `aService` has a matching Server sub-TLV + // same as `aServer`. + + bool contains = false; + const ServerTlv *server; + + VerifyOrExit(aService != NULL); + + for (const NetworkDataTlv *start = aService->GetSubTlvs(); + (server = FindTlv(start, aService->GetNext(), aServer.IsStable())) != NULL; + start = server->GetNext()) + { + if (*server == aServer) + { + ExitNow(contains = true); + } + } + +exit: + return contains; +} + +Leader::UpdateStatus Leader::UpdatePrefix(PrefixTlv &aPrefix) +{ + return UpdateTlv(aPrefix, aPrefix.GetSubTlvs()); +} + +Leader::UpdateStatus Leader::UpdateService(ServiceTlv &aService) +{ + return UpdateTlv(aService, aService.GetSubTlvs()); +} + +Leader::UpdateStatus Leader::UpdateTlv(NetworkDataTlv &aTlv, const NetworkDataTlv *aSubTlvs) +{ + // If `aTlv` contains no sub-TLVs, remove it from Network Data, + // otherwise update its stable flag based on its sub-TLVs. + + UpdateStatus status = kTlvUpdated; + + if (aSubTlvs == aTlv.GetNext()) + { + RemoveTlv(&aTlv); + ExitNow(status = kTlvRemoved); + } + + for (const NetworkDataTlv *subCur = aSubTlvs; subCur < aTlv.GetNext(); subCur = subCur->GetNext()) + { + if (subCur->IsStable()) + { + aTlv.SetStable(); + ExitNow(); + } + } + + aTlv.ClearStable(); + +exit: + return status; +} + +otError Leader::RegisterNetworkData(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength) +{ + otError error = OT_ERROR_NONE; + const NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); + ChangedFlags flags; + + VerifyOrExit(Get().IsAllocated(Mle::Mle::RouterIdFromRloc16(aRloc16)), error = OT_ERROR_NO_ROUTE); + + // Validate that the `aTlvs` contains well-formed TLVs, sub-TLVs, + // and entries all matching `aRloc16` (no other RLOCs). + SuccessOrExit(error = Validate(aTlvs, aTlvsLength, aRloc16)); + + // Remove all entries matching `aRloc16` excluding entries that are + // present in `aTlvs` + RemoveRloc(aRloc16, kMatchModeRloc16, aTlvs, aTlvsLength, flags); + + // Now add all new entries in `aTlvs` to Network Data. + for (const NetworkDataTlv *cur = reinterpret_cast(aTlvs); cur < end; cur = cur->GetNext()) + { switch (cur->GetType()) { case NetworkDataTlv::kTypePrefix: - SuccessOrExit(error = AddPrefix(*static_cast(cur))); - otDumpDebgNetData("add prefix done", mTlvs, mLength); + SuccessOrExit(error = AddPrefix(*static_cast(cur), flags)); break; case NetworkDataTlv::kTypeService: - SuccessOrExit(error = AddService(*static_cast(cur), aOldTlvs, aOldTlvsLength)); - otDumpDebgNetData("add service done", mTlvs, mLength); + SuccessOrExit(error = AddService(*static_cast(cur), flags)); break; default: @@ -770,114 +724,115 @@ otError Leader::AddNetworkData(uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aOl } } + IncrementVersions(flags); + otDumpDebgNetData("add done", mTlvs, mLength); exit: return error; } -otError Leader::AddPrefix(PrefixTlv &aPrefix) +otError Leader::AddPrefix(const PrefixTlv &aPrefix, ChangedFlags &aChangedFlags) { - otError error = OT_ERROR_NONE; - NetworkDataTlv *end; - - VerifyOrExit(aPrefix.IsValid(), error = OT_ERROR_PARSE); - end = aPrefix.GetNext(); - - for (NetworkDataTlv *cur = aPrefix.GetSubTlvs(); cur < end; cur = cur->GetNext()) - { - VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); - - switch (cur->GetType()) - { - case NetworkDataTlv::kTypeHasRoute: - SuccessOrExit(error = AddHasRoute(aPrefix, *static_cast(cur))); - break; - - case NetworkDataTlv::kTypeBorderRouter: - SuccessOrExit(error = AddBorderRouter(aPrefix, *static_cast(cur))); - break; - - default: - break; - } - } - -exit: - return error; -} - -otError Leader::AddService(ServiceTlv &aService, uint8_t *aOldTlvs, uint8_t aOldTlvsLength) -{ - otError error = OT_ERROR_NONE; - NetworkDataTlv *end; - - VerifyOrExit(aService.IsValid(), error = OT_ERROR_PARSE); - end = aService.GetNext(); - - for (NetworkDataTlv *cur = aService.GetSubTlvs(); cur < end; cur = cur->GetNext()) - { - VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end, error = OT_ERROR_PARSE); - - switch (cur->GetType()) - { - case NetworkDataTlv::kTypeServer: - SuccessOrExit(error = AddServer(aService, *static_cast(cur), aOldTlvs, aOldTlvsLength)); - break; - - default: - break; - } - } - -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()); - } + otError error = OT_ERROR_NONE; + PrefixTlv *dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength()); if (dstPrefix == NULL) { - appendLength += sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()); - } + dstPrefix = static_cast(AppendTlv(PrefixTlv::CalculateSize(aPrefix.GetPrefixLength()))); + VerifyOrExit(dstPrefix != NULL, error = OT_ERROR_NO_BUFS); - if (dstHasRoute == NULL) - { - appendLength += sizeof(HasRouteTlv); - } - - appendLength += sizeof(HasRouteEntry); - - VerifyOrExit(mLength + appendLength <= sizeof(mTlvs), error = OT_ERROR_NO_BUFS); - - if (dstPrefix == NULL) - { - dstPrefix = static_cast(AppendTlv(sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()))); dstPrefix->Init(aPrefix.GetDomainId(), aPrefix.GetPrefixLength(), aPrefix.GetPrefix()); } - if (aHasRoute.IsStable()) + for (const NetworkDataTlv *subCur = aPrefix.GetSubTlvs(); subCur < aPrefix.GetNext(); subCur = subCur->GetNext()) { - dstPrefix->SetStable(); + switch (subCur->GetType()) + { + case NetworkDataTlv::kTypeHasRoute: + SuccessOrExit(error = AddHasRoute(*static_cast(subCur), *dstPrefix, aChangedFlags)); + break; + + case NetworkDataTlv::kTypeBorderRouter: + SuccessOrExit( + error = AddBorderRouter(*static_cast(subCur), *dstPrefix, aChangedFlags)); + break; + + default: + break; + } } +exit: + if (dstPrefix != NULL) + { + // `UpdatePrefix()` updates the TLV's stable flag based on + // its sub-TLVs, or removes the TLV if it contains no sub-TLV. + // This is called at `exit` to ensure that if appending + // sub-TLVs fail (e.g., out of space in network data), we + // remove an empty Prefix TLV. + + IgnoreReturnValue(UpdatePrefix(*dstPrefix)); + } + + return error; +} + +otError Leader::AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags) +{ + otError error = OT_ERROR_NONE; + ServiceTlv *dstService = + FindService(aService.GetEnterpriseNumber(), aService.GetServiceData(), aService.GetServiceDataLength()); + const ServerTlv *server; + + if (dstService == NULL) + { + uint8_t serviceId; + + SuccessOrExit(error = AllocateServiceId(serviceId)); + + dstService = static_cast( + AppendTlv(ServiceTlv::CalculateSize(aService.GetEnterpriseNumber(), aService.GetServiceDataLength()))); + VerifyOrExit(dstService != NULL, error = OT_ERROR_NO_BUFS); + + dstService->Init(serviceId, aService.GetEnterpriseNumber(), aService.GetServiceData(), + aService.GetServiceDataLength()); + } + + server = FindTlv(aService.GetSubTlvs(), aService.GetNext()); + OT_ASSERT(server != NULL); + + SuccessOrExit(error = AddServer(*server, *dstService, aChangedFlags)); + +exit: + if (dstService != NULL) + { + // `UpdateService()` updates the TLV's stable flag based on + // its sub-TLVs, or removes the TLV if it contains no sub-TLV. + // This is called at `exit` to ensure that if appending + // sub-TLVs fail (e.g., out of space in network data), we + // remove an empty Service TLV. + + IgnoreReturnValue(UpdateService(*dstService)); + } + + return error; +} + +otError Leader::AddHasRoute(const HasRouteTlv &aHasRoute, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags) +{ + otError error = OT_ERROR_NONE; + HasRouteTlv * dstHasRoute = FindHasRoute(aDstPrefix, aHasRoute.IsStable()); + const HasRouteEntry *entry = aHasRoute.GetFirstEntry(); + if (dstHasRoute == NULL) { - dstHasRoute = static_cast(dstPrefix->GetNext()); + // Ensure there is space for `HasRouteTlv` and a single entry. + VerifyOrExit(CanInsert(sizeof(HasRouteTlv) + sizeof(HasRouteEntry)), error = OT_ERROR_NO_BUFS); + + dstHasRoute = static_cast(aDstPrefix.GetNext()); Insert(dstHasRoute, sizeof(HasRouteTlv)); - dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(HasRouteTlv)); + aDstPrefix.IncreaseLength(sizeof(HasRouteTlv)); dstHasRoute->Init(); if (aHasRoute.IsStable()) @@ -886,95 +841,136 @@ otError Leader::AddHasRoute(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute) } } + VerifyOrExit(!ContainsMatchingEntry(dstHasRoute, *entry)); + + VerifyOrExit(CanInsert(sizeof(HasRouteEntry)), error = OT_ERROR_NO_BUFS); + Insert(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)); + dstHasRoute->IncreaseLength(sizeof(HasRouteEntry)); + aDstPrefix.IncreaseLength(sizeof(HasRouteEntry)); + + *dstHasRoute->GetLastEntry() = *entry; + aChangedFlags.Update(*dstHasRoute); exit: return error; } -otError Leader::AddServer(ServiceTlv &aService, ServerTlv &aServer, uint8_t *aOldTlvs, uint8_t aOldTlvsLength) +otError Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, + PrefixTlv & aDstPrefix, + ChangedFlags & aChangedFlags) { - otError error = OT_ERROR_NONE; - ServiceTlv *dstService = NULL; - ServiceTlv *oldService = NULL; - ServerTlv * dstServer = NULL; - uint16_t appendLength = 0; - uint8_t serviceId = 0; - uint16_t serviceTlvSize = ServiceTlv::GetSize(aService.GetEnterpriseNumber(), aService.GetServiceDataLength()); + otError error = OT_ERROR_NONE; + BorderRouterTlv * dstBorderRouter = FindBorderRouter(aDstPrefix, aBorderRouter.IsStable()); + ContextTlv * dstContext = FindContext(aDstPrefix); + uint8_t contextId = 0; + const BorderRouterEntry *entry = aBorderRouter.GetFirstEntry(); - dstService = - FindService(aService.GetEnterpriseNumber(), aService.GetServiceData(), aService.GetServiceDataLength()); - - if (dstService == NULL) + if (dstContext == NULL) { - appendLength += serviceTlvSize; + // Allocate a Context ID first. This ensure that if we cannot + // allocate, we fail and exit before potentially inserting a + // Border Router sub-TLV. + SuccessOrExit(error = AllocateContextId(contextId)); } - appendLength += sizeof(ServerTlv) + aServer.GetServerDataLength(); - - VerifyOrExit(mLength + appendLength <= sizeof(mTlvs), error = OT_ERROR_NO_BUFS); - - if (dstService == NULL) + if (dstBorderRouter == NULL) { - // Try to preserve old Service ID, if existing - oldService = FindService(aService.GetEnterpriseNumber(), aService.GetServiceData(), - aService.GetServiceDataLength(), aOldTlvs, aOldTlvsLength); + // Ensure there is space for `BorderRouterTlv` with a single entry + // and a `ContextTlv` (if not already present). + VerifyOrExit(CanInsert(sizeof(BorderRouterTlv) + sizeof(BorderRouterEntry) + + ((dstContext == NULL) ? sizeof(ContextTlv) : 0)), + error = OT_ERROR_NO_BUFS); - if (oldService != NULL) + dstBorderRouter = static_cast(aDstPrefix.GetNext()); + Insert(dstBorderRouter, sizeof(BorderRouterTlv)); + aDstPrefix.IncreaseLength(sizeof(BorderRouterTlv)); + dstBorderRouter->Init(); + + if (aBorderRouter.IsStable()) { - // 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(); + dstBorderRouter->SetStable(); } - 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 = static_cast(AppendTlv(static_cast(serviceTlvSize))); - - dstService->Init(serviceId, aService.GetEnterpriseNumber(), aService.GetServiceData(), - aService.GetServiceDataLength()); } - dstServer = static_cast(dstService->GetNext()); + if (dstContext == NULL) + { + // Ensure there is space for a `ContextTlv` and a single entry. + VerifyOrExit(CanInsert(sizeof(BorderRouterEntry) + sizeof(ContextTlv)), error = OT_ERROR_NO_BUFS); - Insert(dstServer, sizeof(ServerTlv) + aServer.GetServerDataLength()); - dstServer->Init(); - dstServer->SetServer16(aServer.GetServer16()); - dstServer->SetServerData(aServer.GetServerData(), aServer.GetServerDataLength()); + dstContext = static_cast(aDstPrefix.GetNext()); + Insert(dstContext, sizeof(ContextTlv)); + aDstPrefix.IncreaseLength(sizeof(ContextTlv)); + dstContext->Init(static_cast(contextId), aDstPrefix.GetPrefixLength()); + } + + if (aBorderRouter.IsStable()) + { + dstContext->SetStable(); + } + + dstContext->SetCompress(); + StopContextReuseTimer(dstContext->GetContextId()); + + VerifyOrExit(!ContainsMatchingEntry(dstBorderRouter, *entry)); + + VerifyOrExit(CanInsert(sizeof(BorderRouterEntry)), error = OT_ERROR_NO_BUFS); + + Insert(dstBorderRouter->GetNext(), sizeof(BorderRouterEntry)); + dstBorderRouter->IncreaseLength(sizeof(BorderRouterEntry)); + aDstPrefix.IncreaseLength(sizeof(BorderRouterEntry)); + *dstBorderRouter->GetLastEntry() = *entry; + aChangedFlags.Update(*dstBorderRouter); + +exit: + return error; +} + +otError Leader::AddServer(const ServerTlv &aServer, ServiceTlv &aDstService, ChangedFlags &aChangedFlags) +{ + otError error = OT_ERROR_NONE; + ServerTlv *dstServer; + uint8_t tlvSize = aServer.GetSize(); + + VerifyOrExit(!ContainsMatchingServer(&aDstService, aServer)); + + VerifyOrExit(CanInsert(tlvSize), error = OT_ERROR_NO_BUFS); + + dstServer = static_cast(aDstService.GetNext()); + Insert(dstServer, tlvSize); + dstServer->Init(aServer.GetServer16(), aServer.GetServerData(), aServer.GetServerDataLength()); if (aServer.IsStable()) { - dstService->SetStable(); dstServer->SetStable(); } - dstService->SetLength(dstService->GetLength() + sizeof(ServerTlv) + aServer.GetServerDataLength()); + aDstService.IncreaseLength(tlvSize); + aChangedFlags.Update(*dstServer); exit: return error; } +otError Leader::AllocateServiceId(uint8_t &aServiceId) +{ + otError error = OT_ERROR_NOT_FOUND; + uint8_t serviceId; + + for (serviceId = Mle::kServiceMinId; serviceId <= Mle::kServiceMaxId; serviceId++) + { + if (FindServiceById(serviceId) == NULL) + { + aServiceId = serviceId; + error = OT_ERROR_NONE; + otLogInfoNetData("Allocated Service ID = %d", serviceId); + break; + } + } + + return error; +} + const ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) const { const NetworkDataTlv *start = GetTlvsStart(); @@ -994,113 +990,26 @@ exit: return service; } -otError Leader::AddBorderRouter(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter) +otError Leader::AllocateContextId(uint8_t &aContextId) { - otError error = OT_ERROR_NONE; - PrefixTlv * dstPrefix = NULL; - ContextTlv * dstContext = NULL; - BorderRouterTlv *dstBorderRouter = NULL; - int contextId = -1; - uint16_t appendLength = 0; + otError error = OT_ERROR_NOT_FOUND; - VerifyOrExit(aBorderRouter.GetNumEntries() > 0, error = OT_ERROR_PARSE); - - if ((dstPrefix = FindPrefix(aPrefix.GetPrefix(), aPrefix.GetPrefixLength())) != NULL) + for (uint8_t contextId = kMinContextId; contextId < kMinContextId + kNumContextIds; contextId++) { - 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 = static_cast(AppendTlv(sizeof(PrefixTlv) + BitVectorBytes(aPrefix.GetPrefixLength()))); - dstPrefix->Init(aPrefix.GetDomainId(), aPrefix.GetPrefixLength(), aPrefix.GetPrefix()); - } - - if (dstContext == NULL) - { - dstContext = static_cast(dstPrefix->GetNext()); - Insert(dstContext, sizeof(ContextTlv)); - dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(ContextTlv)); - dstContext->Init(); - dstContext->SetCompress(); - dstContext->SetContextId(static_cast(contextId)); - dstContext->SetContextLength(aPrefix.GetPrefixLength()); - } - - dstContext->SetCompress(); - StopContextReuseTimer(dstContext->GetContextId()); - - if (dstBorderRouter == NULL) - { - dstBorderRouter = static_cast(dstPrefix->GetNext()); - Insert(dstBorderRouter, sizeof(BorderRouterTlv)); - dstPrefix->SetLength(dstPrefix->GetLength() + sizeof(BorderRouterTlv)); - dstBorderRouter->Init(); - } - - Insert(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) + if ((mContextUsed & (1 << contextId)) == 0) { - mContextUsed |= 1 << i; - rval = i; - otLogInfoNetData("Allocated Context ID = %d", rval); - ExitNow(); + mContextUsed |= (1 << contextId); + aContextId = contextId; + error = OT_ERROR_NONE; + otLogInfoNetData("Allocated Context ID = %d", contextId); + break; } } -exit: - return rval; + return error; } -void Leader::FreeContext(uint8_t aContextId) +void Leader::FreeContextId(uint8_t aContextId) { otLogInfoNetData("Free Context Id = %d", aContextId); RemoveContext(aContextId); @@ -1125,8 +1034,23 @@ void Leader::StopContextReuseTimer(uint8_t aContextId) mContextLastUsed[aContextId - kMinContextId].SetValue(0); } -void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode) +void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags) { + RemoveRloc(aRloc16, aMatchMode, NULL, 0, aChangedFlags); +} + +void Leader::RemoveRloc(uint16_t aRloc16, + MatchMode aMatchMode, + const uint8_t *aExcludeTlvs, + uint8_t aExcludeTlvsLength, + ChangedFlags & aChangedFlags) +{ + // Remove entries from Network Data matching `aRloc16` (using + // `aMatchMode` to determine the match) but exclude any entries + // that are present in `aExcludeTlvs`. As entries are removed + // update `aChangedFlags` to indicate if Network Data (stable or + // not) got changed. + NetworkDataTlv *cur = GetTlvsStart(); while (cur < GetTlvsEnd()) @@ -1135,34 +1059,36 @@ void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode) { case NetworkDataTlv::kTypePrefix: { - PrefixTlv *prefix = static_cast(cur); + PrefixTlv * prefix = static_cast(cur); + const PrefixTlv *excludePrefix = + FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength(), aExcludeTlvs, aExcludeTlvsLength); - RemoveRloc(*prefix, aRloc16, aMatchMode); + RemoveRlocInPrefix(*prefix, aRloc16, aMatchMode, excludePrefix, aChangedFlags); - if (prefix->GetSubTlvsLength() == 0) + if (UpdatePrefix(*prefix) == kTlvRemoved) { - RemoveTlv(prefix); + // Do not update `cur` when TLV is removed. continue; } - otDumpDebgNetData("remove prefix done", mTlvs, mLength); break; } case NetworkDataTlv::kTypeService: { - ServiceTlv *service = static_cast(cur); + ServiceTlv * service = static_cast(cur); + const ServiceTlv *excludeService = + FindService(service->GetEnterpriseNumber(), service->GetServiceData(), service->GetServiceDataLength(), + aExcludeTlvs, aExcludeTlvsLength); - RemoveRloc(*service, aRloc16, aMatchMode); + RemoveRlocInService(*service, aRloc16, aMatchMode, excludeService, aChangedFlags); - if (service->GetSubTlvsLength() == 0) + if (UpdateService(*service) == kTlvRemoved) { - RemoveTlv(service); + // Do not update `cur` when TLV is removed. continue; } - otDumpDebgNetData("remove service done", mTlvs, mLength); - break; } @@ -1176,8 +1102,15 @@ void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode) otDumpDebgNetData("remove done", mTlvs, mLength); } -void Leader::RemoveRloc(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMode) +void Leader::RemoveRlocInPrefix(PrefixTlv & aPrefix, + uint16_t aRloc16, + MatchMode aMatchMode, + const PrefixTlv *aExcludePrefix, + ChangedFlags & aChangedFlags) { + // Remove entries in `aPrefix` TLV matching the given `aRloc16` + // excluding any entries that are present in `aExcludePrefix`. + NetworkDataTlv *cur = aPrefix.GetSubTlvs(); ContextTlv * context; @@ -1186,12 +1119,12 @@ void Leader::RemoveRloc(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMo switch (cur->GetType()) { case NetworkDataTlv::kTypeHasRoute: - RemoveRloc(aPrefix, *static_cast(cur), aRloc16, aMatchMode); + RemoveRlocInHasRoute(aPrefix, *static_cast(cur), aRloc16, aMatchMode, aExcludePrefix, + aChangedFlags); - // remove has route tlv if empty if (cur->GetLength() == 0) { - aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(HasRouteTlv)); + aPrefix.DecreaseLength(sizeof(HasRouteTlv)); RemoveTlv(cur); continue; } @@ -1199,12 +1132,12 @@ void Leader::RemoveRloc(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMo break; case NetworkDataTlv::kTypeBorderRouter: - RemoveRloc(aPrefix, *static_cast(cur), aRloc16, aMatchMode); + RemoveRlocInBorderRouter(aPrefix, *static_cast(cur), aRloc16, aMatchMode, aExcludePrefix, + aChangedFlags); - // remove border router tlv if empty if (cur->GetLength() == 0) { - aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(BorderRouterTlv)); + aPrefix.DecreaseLength(sizeof(BorderRouterTlv)); RemoveTlv(cur); continue; } @@ -1233,18 +1166,27 @@ void Leader::RemoveRloc(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMo } } -void Leader::RemoveRloc(ServiceTlv &aService, uint16_t aRloc16, MatchMode aMatchMode) +void Leader::RemoveRlocInService(ServiceTlv & aService, + uint16_t aRloc16, + MatchMode aMatchMode, + const ServiceTlv *aExcludeService, + ChangedFlags & aChangedFlags) { + // Remove entries in `aService` TLV matching the given `aRloc16` + // excluding any entries that are present in `aExcludeService`. + NetworkDataTlv *start = aService.GetSubTlvs(); ServerTlv * server; while ((server = FindTlv(start, aService.GetNext())) != NULL) { - if (RlocMatch(server->GetServer16(), aRloc16, aMatchMode)) + if (RlocMatch(server->GetServer16(), aRloc16, aMatchMode) && !ContainsMatchingServer(aExcludeService, *server)) { uint8_t subTlvSize = server->GetSize(); + + aChangedFlags.Update(*server); RemoveTlv(server); - aService.SetSubTlvsLength(aService.GetSubTlvsLength() - subTlvSize); + aService.DecreaseLength(subTlvSize); continue; } @@ -1252,16 +1194,27 @@ void Leader::RemoveRloc(ServiceTlv &aService, uint16_t aRloc16, MatchMode aMatch } } -void Leader::RemoveRloc(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute, uint16_t aRloc16, MatchMode aMatchMode) +void Leader::RemoveRlocInHasRoute(PrefixTlv & aPrefix, + HasRouteTlv & aHasRoute, + uint16_t aRloc16, + MatchMode aMatchMode, + const PrefixTlv *aExcludePrefix, + ChangedFlags & aChangedFlags) { + // Remove entries in `aHasRoute` (a sub-TLV of `aPrefix` TLV) + // matching the given `aRloc16` excluding entries that are present + // in `aExcludePrefix`. + HasRouteEntry *entry = aHasRoute.GetFirstEntry(); while (entry <= aHasRoute.GetLastEntry()) { - if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode)) + if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode) && + !ContainsMatchingEntry(aExcludePrefix, aHasRoute.IsStable(), *entry)) { - aHasRoute.SetLength(aHasRoute.GetLength() - sizeof(HasRouteEntry)); - aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(HasRouteEntry)); + aChangedFlags.Update(aHasRoute); + aHasRoute.DecreaseLength(sizeof(HasRouteEntry)); + aPrefix.DecreaseLength(sizeof(HasRouteEntry)); Remove(entry, sizeof(HasRouteEntry)); continue; } @@ -1270,16 +1223,27 @@ void Leader::RemoveRloc(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute, uint16_t aRl } } -void Leader::RemoveRloc(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter, uint16_t aRloc16, MatchMode aMatchMode) +void Leader::RemoveRlocInBorderRouter(PrefixTlv & aPrefix, + BorderRouterTlv &aBorderRouter, + uint16_t aRloc16, + MatchMode aMatchMode, + const PrefixTlv *aExcludePrefix, + ChangedFlags & aChangedFlags) { + // Remove entries in `aBorderRouter` (a sub-TLV of `aPrefix` TLV) + // matching the given `aRloc16` excluding entries that are present + // in `aExcludePrefix`. + BorderRouterEntry *entry = aBorderRouter.GetFirstEntry(); while (entry <= aBorderRouter.GetLastEntry()) { - if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode)) + if (RlocMatch(entry->GetRloc(), aRloc16, aMatchMode) && + !ContainsMatchingEntry(aExcludePrefix, aBorderRouter.IsStable(), *entry)) { - aBorderRouter.SetLength(aBorderRouter.GetLength() - sizeof(BorderRouterEntry)); - aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - sizeof(BorderRouterEntry)); + aChangedFlags.Update(aBorderRouter); + aBorderRouter.DecreaseLength(sizeof(BorderRouterEntry)); + aPrefix.DecreaseLength(sizeof(BorderRouterEntry)); Remove(entry, sizeof(*entry)); continue; } @@ -1297,16 +1261,14 @@ void Leader::RemoveContext(uint8_t aContextId) { RemoveContext(*prefix, aContextId); - if (prefix->GetSubTlvsLength() == 0) + if (UpdatePrefix(*prefix) == kTlvRemoved) { - RemoveTlv(prefix); + // Do not update `start` when TLV is removed. continue; } start = prefix->GetNext(); } - - otDumpDebgNetData("remove done", mTlvs, mLength); } void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId) @@ -1320,7 +1282,7 @@ void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId) { uint8_t subTlvSize = context->GetSize(); RemoveTlv(context); - aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - subTlvSize); + aPrefix.DecreaseLength(subTlvSize); continue; } @@ -1373,7 +1335,7 @@ void Leader::HandleTimer(void) if (TimerMilli::GetNow() - mContextLastUsed[i] >= Time::SecToMsec(mContextIdReuseDelay)) { - FreeContext(kMinContextId + i); + FreeContextId(kMinContextId + i); } else { diff --git a/src/core/thread/network_data_leader_ftd.hpp b/src/core/thread/network_data_leader_ftd.hpp index a70f08044..f3bfa4af6 100644 --- a/src/core/thread/network_data_leader_ftd.hpp +++ b/src/core/thread/network_data_leader_ftd.hpp @@ -171,51 +171,105 @@ public: otError RemoveStaleChildEntries(Coap::ResponseHandler aHandler, void *aContext); private: + class ChangedFlags + { + public: + ChangedFlags(void) + : mChanged(false) + , mStableChanged(false) + { + } + + void Update(const NetworkDataTlv &aTlv) + { + mChanged = true; + mStableChanged = (mStableChanged || aTlv.IsStable()); + } + + bool DidChange(void) const { return mChanged; } + bool DidStableChange(void) const { return mStableChanged; } + + private: + bool mChanged; // Any (stable or not) network data change (add/remove). + bool mStableChanged; // Stable network data change (add/remove). + }; + + enum UpdateStatus + { + kTlvRemoved, // TLV contained no sub TLVs and therefore is removed. + kTlvUpdated, // TLV stable flag is updated based on its sub TLVs. + }; + static void HandleServerData(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleServerData(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandleTimer(Timer &aTimer); void HandleTimer(void); - otError RegisterNetworkData(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength); + otError RegisterNetworkData(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsLength); - otError AddHasRoute(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute); - otError AddBorderRouter(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter); - otError AddNetworkData(uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aOldTlvs, uint8_t aOldTlvsLength); - otError AddPrefix(PrefixTlv &aPrefix); - otError AddServer(ServiceTlv &aService, ServerTlv &aServer, uint8_t *aOldTlvs, uint8_t aOldTlvsLength); - otError AddService(ServiceTlv &aService, uint8_t *aOldTlvs, uint8_t aOldTlvsLength); + otError AddPrefix(const PrefixTlv &aPrefix, ChangedFlags &aChangedFlags); + otError AddHasRoute(const HasRouteTlv &aHasRoute, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags); + otError AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &aDstPrefix, ChangedFlags &aFlags); + otError AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags); + otError AddServer(const ServerTlv &aServer, ServiceTlv &aDstService, ChangedFlags &aChangedFlags); - int AllocateContext(void); - void FreeContext(uint8_t aContextId); - void StartContextReuseTimer(uint8_t aContextId); - void StopContextReuseTimer(uint8_t aContextId); + otError AllocateServiceId(uint8_t &aServiceId); + + otError AllocateContextId(uint8_t &aConextId); + void FreeContextId(uint8_t aContextId); + void StartContextReuseTimer(uint8_t aContextId); + void StopContextReuseTimer(uint8_t aContextId); void RemoveContext(uint8_t aContextId); void RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId); void RemoveCommissioningData(void); - void RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode); - void RemoveRloc(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMode); - void RemoveRloc(ServiceTlv &aService, uint16_t aRloc16, MatchMode aMatchMode); - void RemoveRloc(PrefixTlv &aPrefix, HasRouteTlv &aHasRoute, uint16_t aRloc16, MatchMode aMatchMode); - void RemoveRloc(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter, uint16_t aRloc16, MatchMode aMatchMode); + void RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags); + void RemoveRloc(uint16_t aRloc16, + MatchMode aMatchMode, + const uint8_t *aExcludeTlvs, + uint8_t aExcludeTlvsLength, + ChangedFlags & aChangedFlags); + void RemoveRlocInPrefix(PrefixTlv & aPrefix, + uint16_t aRloc16, + MatchMode aMatchMode, + const PrefixTlv *aExcludePrefix, + ChangedFlags & aChangedFlags); + void RemoveRlocInService(ServiceTlv & aService, + uint16_t aRloc16, + MatchMode aMatchMode, + const ServiceTlv *aExcludeService, + ChangedFlags & aChangedFlags); + void RemoveRlocInHasRoute(PrefixTlv & aPrefix, + HasRouteTlv & aHasRoute, + uint16_t aRloc16, + MatchMode aMatchMode, + const PrefixTlv *aExcludePrefix, + ChangedFlags & aChangedFlags); + void RemoveRlocInBorderRouter(PrefixTlv & aPrefix, + BorderRouterTlv &aBorderRouter, + uint16_t aRloc16, + MatchMode aMatchMode, + const PrefixTlv *aExcludePrefix, + ChangedFlags & aChangedFlags); static bool RlocMatch(uint16_t aFirstRloc16, uint16_t aSecondRloc16, MatchMode aMatchMode); - static otError RlocLookup(uint16_t aRloc16, - bool & aIn, - bool & aStable, - const uint8_t *aTlvs, - uint8_t aTlvsLength, - MatchMode aMatchMode, - bool aAllowOtherEntries = true); + static otError Validate(const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aRloc16); + static otError ValidatePrefix(const PrefixTlv &aPrefix, uint16_t aRloc16); + static otError ValidateService(const ServiceTlv &aService, uint16_t aRloc16); - static bool IsStableUpdated(const uint8_t *aTlvs, - uint8_t aTlvsLength, - const uint8_t *aTlvsBase, - uint8_t aTlvsBaseLength); + static bool ContainsMatchingEntry(const PrefixTlv *aPrefix, bool aStable, const HasRouteEntry &aEntry); + static bool ContainsMatchingEntry(const HasRouteTlv *aHasRoute, const HasRouteEntry &aEntry); + static bool ContainsMatchingEntry(const PrefixTlv *aPrefix, bool aStable, const BorderRouterEntry &aEntry); + static bool ContainsMatchingEntry(const BorderRouterTlv *aBorderRouter, const BorderRouterEntry &aEntry); + static bool ContainsMatchingServer(const ServiceTlv *aService, const ServerTlv &aServer); + + UpdateStatus UpdatePrefix(PrefixTlv &aPrefix); + UpdateStatus UpdateService(ServiceTlv &aService); + UpdateStatus UpdateTlv(NetworkDataTlv &aTlv, const NetworkDataTlv *aSubTlvs); static void HandleCommissioningSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleCommissioningSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); @@ -230,6 +284,7 @@ private: const Ip6::MessageInfo & aMessageInfo, MeshCoP::StateTlv::State aState); void IncrementVersions(bool aIncludeStable); + void IncrementVersions(const ChangedFlags &aFlags); /** * Thread Specification Constants. diff --git a/src/core/thread/network_data_local.cpp b/src/core/thread/network_data_local.cpp index 63a039751..44fa3b3e5 100644 --- a/src/core/thread/network_data_local.cpp +++ b/src/core/thread/network_data_local.cpp @@ -251,21 +251,22 @@ otError Local::AddService(uint32_t aEnterpriseNumber, otError error = OT_ERROR_NONE; ServiceTlv *serviceTlv; ServerTlv * serverTlv; - size_t serviceTlvSize = - ServiceTlv::GetSize(aEnterpriseNumber, aServiceDataLength) + sizeof(ServerTlv) + aServerDataLength; + uint16_t serviceTlvSize = + ServiceTlv::CalculateSize(aEnterpriseNumber, aServiceDataLength) + sizeof(ServerTlv) + aServerDataLength; RemoveService(aEnterpriseNumber, aServiceData, aServiceDataLength); VerifyOrExit(serviceTlvSize <= kMaxSize, error = OT_ERROR_NO_BUFS); - serviceTlv = static_cast(AppendTlv(static_cast(serviceTlvSize))); + serviceTlv = static_cast(AppendTlv(serviceTlvSize)); VerifyOrExit(serviceTlv != NULL, error = OT_ERROR_NO_BUFS); serviceTlv->Init(/* aServiceId */ 0, aEnterpriseNumber, aServiceData, aServiceDataLength); serviceTlv->SetSubTlvsLength(sizeof(ServerTlv) + aServerDataLength); serverTlv = static_cast(serviceTlv->GetSubTlvs()); - serverTlv->Init(); + + serverTlv->Init(Get().GetRloc16(), aServerData, aServerDataLength); // According to Thread spec 1.1.1, section 5.18.6 Service TLV: // "The Stable flag is set if any of the included sub-TLVs have their Stable flag set." @@ -276,9 +277,6 @@ otError Local::AddService(uint32_t aEnterpriseNumber, serverTlv->SetStable(); } - serverTlv->SetServer16(Get().GetRloc16()); - serverTlv->SetServerData(aServerData, aServerDataLength); - otDumpDebgNetData("add service done", mTlvs, mLength); exit: diff --git a/src/core/thread/network_data_tlvs.hpp b/src/core/thread/network_data_tlvs.hpp index 7c8e96001..b25309f3e 100644 --- a/src/core/thread/network_data_tlvs.hpp +++ b/src/core/thread/network_data_tlvs.hpp @@ -123,6 +123,22 @@ public: */ void SetLength(uint8_t aLength) { mLength = aLength; } + /** + * This methods increases the Length value by a given amount. + * + * @param[in] aIncrement The increment amount to increase the length. + * + */ + void IncreaseLength(uint8_t aIncrement) { mLength += aIncrement; } + + /** + * This methods decreases the Length value by a given amount. + * + * @param[in] aDecrement The decrement amount to decrease the length. + * + */ + void DecreaseLength(uint8_t aDecrement) { mLength -= aDecrement; } + /** * This method returns the TLV's total size (number of bytes) including Type, Length, and Value fields. * @@ -272,6 +288,20 @@ public: */ const HasRouteEntry *GetNext(void) const { return (this + 1); } + /** + * This method indicates whether two entries fully match. + * + * @param[in] aOtherEntry Another entry to compare with it. + * + * @retval TRUE The two entries are equal. + * @retval FALSE The two entries are not equal. + * + */ + bool operator==(const HasRouteEntry &aOtherEntry) const + { + return (memcmp(this, &aOtherEntry, sizeof(HasRouteEntry)) == 0); + } + private: enum { @@ -506,6 +536,19 @@ public: SetLength(sizeof(*this) - sizeof(NetworkDataTlv) + BitVectorBytes(mPrefixLength) + aLength); } + /** + * This static method calculates the total size (number of bytes) of a Prefix TLV with a given Prefix Length value. + * + * Note that the returned size does include the Type and Length fields in the TLV, but does not account for any + * sub TLVs of the Prefix TLV. + * + * @param[in] aPrefixLength A Prefix Length in bits. + + * @returns The size (number of bytes) of the Prefix TLV. + * + */ + static uint16_t CalculateSize(uint8_t aPrefixLength) { return sizeof(PrefixTlv) + BitVectorBytes(aPrefixLength); } + private: uint8_t mDomainId; uint8_t mPrefixLength; @@ -687,6 +730,20 @@ public: */ const BorderRouterEntry *GetNext(void) const { return (this + 1); } + /** + * This method indicates whether two entries fully match. + * + * @param[in] aOtherEntry Another entry to compare with it. + * + * @retval TRUE The two entries are equal. + * @retval FALSE The two entries are not equal. + * + */ + bool operator==(const BorderRouterEntry &aOtherEntry) const + { + return (memcmp(this, &aOtherEntry, sizeof(BorderRouterEntry)) == 0); + } + private: uint16_t mRloc; uint16_t mFlags; @@ -811,16 +868,19 @@ public: }; /** - * This method initializes the TLV. + * This method initializes the Context TLV. + * + * @param[in] aConextId The Context ID value. + * @param[in] aLength The Context Length value. * */ - void Init(void) + void Init(uint8_t aContextId, uint8_t aConextLength) { NetworkDataTlv::Init(); SetType(kTypeContext); - SetLength(2); - mFlags = 0; - mContextLength = 0; + SetLength(sizeof(ContextTlv) - sizeof(NetworkDataTlv)); + mFlags = ((aContextId << kContextIdOffset) & kContextIdMask); + mContextLength = aConextLength; } /** @@ -852,17 +912,6 @@ public: */ uint8_t GetContextId(void) const { return mFlags & kContextIdMask; } - /** - * This method sets the Context ID value. - * - * @param[in] aContextId The Context ID value. - * - */ - void SetContextId(uint8_t aContextId) - { - mFlags = (mFlags & ~kContextIdMask) | ((aContextId << kContextIdOffset) & kContextIdMask); - } - /** * This method returns the Context Length value. * @@ -871,14 +920,6 @@ public: */ uint8_t GetContextLength(void) const { return mContextLength; } - /** - * This method sets the Context Length value. - * - * @param[in] aLength The Context Length value. - * - */ - void SetContextLength(uint8_t aLength) { mContextLength = aLength; } - private: enum { @@ -1080,7 +1121,7 @@ public: * @returns The size (number of bytes) of the Service TLV. * */ - static uint16_t GetSize(uint32_t aEnterpriseNumber, uint8_t aServiceDataLength) + static uint16_t CalculateSize(uint32_t aEnterpriseNumber, uint8_t aServiceDataLength) { return sizeof(NetworkDataTlv) + kMinLength + aServiceDataLength + ((aEnterpriseNumber == kThreadEnterpriseNumber) ? 0 : sizeof(uint32_t) /* mEnterpriseNumber */); @@ -1133,14 +1174,20 @@ public: }; /** - * This method initializes the TLV. + * This method initializes the Server TLV. + * + * @param[in] aServer16 The Server16 value. + * @param[in] aServerData The Server Data. + * @param[in] aServerDataLength Server Data length in bytes. * */ - void Init(void) + void Init(uint16_t aServer16, const uint8_t *aServerData, uint8_t aServerDataLength) { NetworkDataTlv::Init(); SetType(kTypeServer); - SetLength(sizeof(*this) - sizeof(NetworkDataTlv)); + SetServer16(aServer16); + memcpy(reinterpret_cast(this) + sizeof(*this), aServerData, aServerDataLength); + SetLength(sizeof(*this) - sizeof(NetworkDataTlv) + aServerDataLength); } /** @@ -1153,43 +1200,29 @@ public: bool IsValid(void) const { return GetLength() >= (sizeof(*this) - sizeof(NetworkDataTlv)); } /** - * This method returns the S_server_16 value. + * This method returns the Server16 value. * - * @returns The S_server_16 value. + * @returns The Server16 value. * */ uint16_t GetServer16(void) const { return HostSwap16(mServer16); } - /** - * This method sets the S_server_16 value. + /* + * This method sets the Server16 value. * - * @param[in] aServer16 The S_server_16 value. + * @param[in] aServer16 The Server16 value. * */ void SetServer16(uint16_t aServer16) { mServer16 = HostSwap16(aServer16); } /** - * This method returns a pointer to the Server Data. + * This method returns the Server Data. * * @returns A pointer to the Server Data. * */ const uint8_t *GetServerData(void) const { return reinterpret_cast(this) + sizeof(*this); } - /** - * This method sets Server Data to the given values. - * - * Caller must ensure that there is enough memory allocated. - * - * @param aServerData pointer to the server data to use - * @param aServerDataLength length of the provided server data in bytes - */ - void SetServerData(const uint8_t *aServerData, uint8_t aServerDataLength) - { - SetLength(sizeof(*this) - sizeof(NetworkDataTlv) + aServerDataLength); - memcpy(reinterpret_cast(this) + sizeof(*this), aServerData, aServerDataLength); - } - /** * This method returns the Server Data length in bytes. * @@ -1198,6 +1231,32 @@ public: */ uint8_t GetServerDataLength(void) const { return GetLength() - (sizeof(*this) - sizeof(NetworkDataTlv)); } + /** + * This method indicates whether two Server TLVs fully match. + * + * @param[in] aOther Another Server TLV to compare with it. + * + * @retval TRUE The two TLVs are equal. + * @retval FALSE The two TLVs are not equal. + * + */ + bool operator==(const ServerTlv &aOther) const + { + return (GetLength() == aOther.GetLength()) && (memcmp(GetValue(), aOther.GetValue(), GetLength()) == 0); + } + + /** + * This static method calculates the total size (number of bytes) of a Service TLV with a given Server Data length. + * + * Note that the returned size does include the Type and Length fields in the TLV. + * + * @param[in] aServerDataLength Server Data length in bytes. + * + * @returns The size (number of bytes) of the Server TLV. + * + */ + static uint16_t CalculateSize(uint8_t aServerDataLength) { return sizeof(ServerTlv) + aServerDataLength; } + private: uint16_t mServer16; } OT_TOOL_PACKED_END;