[mle] fix some corner cases when trying to attach to a better partition (#2461)
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index 4455484..e14099c 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -1647,11 +1647,23 @@
Message *message = NULL;
Ip6::Address destination;
- if (mRole == OT_DEVICE_ROLE_CHILD &&
- mParent.GetExtAddress() == mParentCandidate.GetExtAddress())
+ if (mParent.GetExtAddress() == mParentCandidate.GetExtAddress())
{
- otLogInfoMle(GetInstance(), "Already attached to candidate parent");
- ExitNow(error = OT_ERROR_ALREADY);
+ if (mRole == OT_DEVICE_ROLE_CHILD)
+ {
+ otLogInfoMle(GetInstance(), "Already attached to candidate parent");
+ ExitNow(error = OT_ERROR_ALREADY);
+ }
+ else
+ {
+ // Invalidate stale parent state.
+ //
+ // Parent state is not normally invalidated after becoming a Router/Leader (see #1875). When trying to
+ // attach to a better partition, invalidating old parent state (especially when in kStateRestored) ensures
+ // that GetNeighbor() returns mParentCandidate when processing the Child ID Response.
+ mParent.SetState(Neighbor::kStateInvalid);
+ otPlatSettingsDelete(&GetInstance(), Settings::kKeyParentInfo, -1);
+ }
}
VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS);
diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp
index 7094909..582289b 100644
--- a/src/core/thread/mle_router.cpp
+++ b/src/core/thread/mle_router.cpp
@@ -551,7 +551,14 @@
{
otError error = OT_ERROR_NONE;
Ip6::Address destination;
- Message *message;
+ Message *message = NULL;
+
+ // Suppress MLE Advertisements when trying to attach to a better partition.
+ //
+ // Without this suppression, a device may send an MLE Advertisement before receiving the MLE Child ID Response.
+ // The candidate parent then removes the attaching device because the Source Address TLV includes an RLOC16 that
+ // indicates a Router role (i.e. a Child ID equal to zero).
+ VerifyOrExit(mParentRequestState == kParentIdle);
VerifyOrExit((message = NewMleMessage()) != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = AppendHeader(*message, Header::kCommandAdvertisement));