diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index 28f961b0c..a5bd91a7a 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -310,8 +310,13 @@ exit: PrefixTlv *NetworkData::FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { - NetworkDataTlv *cur = reinterpret_cast(mTlvs); - NetworkDataTlv *end = reinterpret_cast(mTlvs + mLength); + return FindPrefix(aPrefix, aPrefixLength, mTlvs, mLength); +} + +PrefixTlv *NetworkData::FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength, uint8_t *aTlvs, uint8_t aTlvsLength) +{ + NetworkDataTlv *cur = reinterpret_cast(aTlvs); + NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); PrefixTlv *compare; while (cur < end) diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp index 85f02417e..9186097c9 100644 --- a/src/core/thread/network_data.hpp +++ b/src/core/thread/network_data.hpp @@ -167,6 +167,19 @@ protected: */ PrefixTlv *FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength); + /** + * This method returns a pointer to a Prefix TLV in a specified tlvs buffer. + * + * @param[in] aPrefix A pointer to an IPv6 prefix. + * @param[in] aPrefixLength The prefix length pointed to by @p aPrefix. + * @param[in] aTlvs A pointer to a specified tlvs buffer. + * @param[in] aTlvsLength The specified tlvs buffer length pointed to by @p aTlvs. + * + * @returns A pointer to the Prefix TLV is one is found or NULL if no matching Prefix TLV exists. + * + */ + PrefixTlv *FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength, uint8_t *aTlvs, uint8_t aTlvsLength); + /** * This method inserts bytes into the Network Data. * diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index b760754db..9c5350c09 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -516,8 +516,23 @@ void Leader::SetNetworkData(uint8_t aVersion, uint8_t aStableVersion, bool aStab void Leader::RemoveBorderRouter(uint16_t aRloc16) { - RemoveRloc(aRloc16); - ConfigureAddresses(); + bool rlocIn = false; + bool rlocStable = false; + RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength); + + if (rlocIn) + { + RemoveRloc(aRloc16); + mVersion++; + + if (rlocStable) + { + mStableVersion++; + } + + ConfigureAddresses(); + } + mMle.HandleNetworkDataUpdate(); } @@ -577,15 +592,178 @@ exit: } } +void Leader::RlocLookup(uint16_t aRloc16, bool &aIn, bool &aStable, uint8_t *aTlvs, uint8_t aTlvsLength) +{ + NetworkDataTlv *cur = reinterpret_cast(aTlvs); + NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); + NetworkDataTlv *subCur; + NetworkDataTlv *subEnd; + PrefixTlv *prefix; + BorderRouterTlv *borderRouter; + HasRouteTlv *hasRoute; + BorderRouterEntry *borderRouterEntry; + HasRouteEntry *hasRouteEntry; + + while (cur < end) + { + if (cur->GetType() == NetworkDataTlv::kTypePrefix) + { + prefix = reinterpret_cast(cur); + subCur = reinterpret_cast(prefix->GetSubTlvs()); + subEnd = reinterpret_cast(prefix->GetSubTlvs() + prefix->GetSubTlvsLength()); + + while (subCur < subEnd) + { + switch (subCur->GetType()) + { + case NetworkDataTlv::kTypeBorderRouter: + borderRouter = FindBorderRouter(*prefix); + + for (int i = 0; i < borderRouter->GetNumEntries(); i++) + { + borderRouterEntry = borderRouter->GetEntry(i); + + if (borderRouterEntry->GetRloc() == aRloc16) + { + aIn = true; + + if (borderRouter->IsStable()) + { + aStable = true; + } + } + } + + break; + + case NetworkDataTlv::kTypeHasRoute: + hasRoute = FindHasRoute(*prefix); + + for (int i = 0; i < hasRoute->GetNumEntries(); i++) + { + hasRouteEntry = hasRoute->GetEntry(i); + + if (hasRouteEntry->GetRloc() == aRloc16) + { + aIn = true; + + if (hasRoute->IsStable()) + { + aStable = true; + } + } + } + + break; + + default: + break; + } + + if (aIn && aStable) + { + ExitNow(); + } + + subCur = subCur->GetNext(); + } + } + + cur = cur->GetNext(); + } + +exit: + return; +} + +bool Leader::IsStableUpdated(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aTlvsBase, + uint8_t aTlvsBaseLength) +{ + bool rval = false; + NetworkDataTlv *cur = reinterpret_cast(aTlvs); + NetworkDataTlv *end = reinterpret_cast(aTlvs + aTlvsLength); + PrefixTlv *prefix; + PrefixTlv *prefixBase; + BorderRouterTlv *borderRouter; + HasRouteTlv *hasRoute; + ContextTlv *context; + + while (cur < end) + { + if (cur->GetType() == NetworkDataTlv::kTypePrefix) + { + prefix = reinterpret_cast(cur); + context = FindContext(*prefix); + borderRouter = FindBorderRouter(*prefix); + hasRoute = FindHasRoute(*prefix); + + if (cur->IsStable() && (!context || borderRouter)) + { + prefixBase = FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength(), aTlvsBase, aTlvsBaseLength); + + if (!prefixBase) + { + ExitNow(rval = true); + } + + if (borderRouter && memcmp(borderRouter, FindBorderRouter(*prefixBase), borderRouter->GetLength()) != 0) + { + ExitNow(rval = true); + } + + if (hasRoute && (memcmp(hasRoute, FindHasRoute(*prefixBase), hasRoute->GetLength()) != 0)) + { + ExitNow(rval = true); + } + } + } + + cur = cur->GetNext(); + } + +exit: + return rval; +} + ThreadError Leader::RegisterNetworkData(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength) { ThreadError error = kThreadError_None; + bool rlocIn = false; + bool rlocStable = false; + bool stableUpdated = false; - SuccessOrExit(error = RemoveRloc(aRloc16)); - SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength)); + RlocLookup(aRloc16, rlocIn, rlocStable, mTlvs, mLength); - mVersion++; - mStableVersion++; + if (rlocIn) + { + if (IsStableUpdated(aRloc16, aTlvs, aTlvsLength, mTlvs, mLength) || + IsStableUpdated(aRloc16, mTlvs, mLength, aTlvs, aTlvsLength)) + { + stableUpdated = true; + } + + SuccessOrExit(error = RemoveRloc(aRloc16)); + SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength)); + + mVersion++; + + if (stableUpdated) + { + mStableVersion++; + } + } + else + { + RlocLookup(aRloc16, rlocIn, rlocStable, aTlvs, aTlvsLength); + SuccessOrExit(error = AddNetworkData(aTlvs, aTlvsLength)); + + mVersion++; + + if (rlocStable) + { + mStableVersion++; + } + } ConfigureAddresses(); mMle.HandleNetworkDataUpdate(); diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index b76a02433..6ef08fc92 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -230,6 +230,10 @@ private: ThreadError ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &destination, uint8_t *aPrefixMatch, uint16_t *aRloc16); ThreadError DefaultRouteLookup(PrefixTlv &aPrefix, uint16_t *aRloc16); + void RlocLookup(uint16_t aRloc16, bool &aIn, bool &aStable, uint8_t *aTlvs, uint8_t aTlvsLength); + bool IsStableUpdated(uint16_t aRloc16, uint8_t *aTlvs, uint8_t aTlvsLength, uint8_t *aTlvsBase, + uint8_t aTlvsBaseLength); + /** * Thread Specification Constants