[mle] introduce AnnounceHandler class (#11558)

This commit introduces the `AnnounceHandler` class within `Mle` to
encapsulate logic for handling Announce messages. This change
simplifies the `Mle` module and makes the code easier to read and
follow.

The `AnnounceHandler` class handles received Announce messages with a
newer timestamp and different channel and/or PAN ID. It can delay
processing to collect and handle subsequent Announce messages.

It also manages starting the "announce attach" process, where the device
attempts to attach using the parameters from a processed Announce
message.
- If the attach is successful, this class sends an Announce on the old
  channel to inform other devices. This is done immediately after
  attaching as a MTD child or after a router transition attempt
  completes (on FTD).
- If the attach fails, the class ensures the channel and PAN ID are
  restored to their original values.

In particular, the new `AnnounceHandler` uses its own `Timer` to delay
the start of an announce attach. Previously, the `AttachTimer` was
repurposed for this, in addition to its use for attach state
management.
This commit is contained in:
Abtin Keshavarzian
2025-06-03 13:33:49 -07:00
committed by GitHub
parent 18c731764a
commit e4ac7162eb
3 changed files with 145 additions and 54 deletions
+79 -39
View File
@@ -67,21 +67,19 @@ Mle::Mle(Instance &aInstance)
, mChildUpdateAttempts(0)
, mDataRequestAttempts(0)
, mAnnounceChannel(0)
, mAlternateChannel(0)
, mRloc16(kInvalidRloc16)
, mPreviousParentRloc(kInvalidRloc16)
, mAttachCounter(0)
, mAnnounceDelay(kAnnounceTimeout)
, mAlternatePanId(Mac::kPanIdBroadcast)
, mStoreFrameCounterAhead(kDefaultStoreFrameCounterAhead)
, mTimeout(kDefaultChildTimeout)
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
, mCslTimeout(kDefaultCslTimeout)
#endif
, mAlternateTimestamp(0)
, mNeighborTable(aInstance)
, mDelayedSender(aInstance)
, mSocket(aInstance, *this)
, mAnnounceHandler(aInstance)
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
, mParentSearch(aInstance)
#endif
@@ -270,6 +268,7 @@ void Mle::Stop(StopMode aMode)
VerifyOrExit(!IsDisabled());
mDelayedSender.Stop();
mAnnounceHandler.Stop();
Get<KeyManager>().Stop();
SetStateDetached();
Get<ThreadNetif>().UnsubscribeMulticast(mRealmLocalAllThreadNodes);
@@ -793,8 +792,10 @@ void Mle::SetStateChild(uint16_t aRloc16)
}
#endif
// send announce after attached if needed
InformPreviousChannel();
if (mAnnounceHandler.IsAnnounceAttaching())
{
mAnnounceHandler.HandleAnnounceAttachSuccess();
}
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
mParentSearch.UpdateState();
@@ -812,16 +813,31 @@ void Mle::SetStateChild(uint16_t aRloc16)
mPreviousParentRloc = mParent.GetRloc16();
}
void Mle::InformPreviousChannel(void)
void Mle::AnnounceHandler::HandleAnnounceAttachSuccess(void)
{
VerifyOrExit(mAlternatePanId != Mac::kPanIdBroadcast);
VerifyOrExit(IsChild() || IsRouter());
// Clear state and send announce on previous channel.
VerifyOrExit(mState == kStateAnnounceAttaching);
mState = kStateToInformPreviousChannel;
#if OPENTHREAD_FTD
VerifyOrExit(!IsFullThreadDevice() || IsRouter() || !IsRouterRoleTransitionPending());
if (Get<Mle>().IsFullThreadDevice() && !Get<Mle>().IsRouter() && Get<Mle>().IsRouterRoleTransitionPending())
{
ExitNow();
}
#endif
mAlternatePanId = Mac::kPanIdBroadcast;
InformPreviousChannel();
exit:
return;
}
void Mle::AnnounceHandler::InformPreviousChannel(void)
{
VerifyOrExit(mState == kStateToInformPreviousChannel);
mState = kStateIdle;
Get<AnnounceBeginServer>().SendAnnounce(1 << mAlternateChannel);
exit:
@@ -1504,10 +1520,6 @@ void Mle::HandleAttachTimer(void)
mAttachCounter = 0;
break;
case kAttachStateProcessAnnounce:
ProcessAnnounce();
break;
case kAttachStateStart:
LogNote("Attach attempt %d, %s %s", mAttachCounter, AttachModeToString(mAttachMode),
ReattachStateToString(mReattachState));
@@ -1640,11 +1652,9 @@ uint32_t Mle::Reattach(void)
case kSelectedParent:
if (!IsChild())
{
if (mAlternatePanId != Mac::kPanIdBroadcast)
if (mAnnounceHandler.IsAnnounceAttaching())
{
IgnoreError(Get<Mac::Mac>().SetPanChannel(mAlternateChannel));
Get<Mac::Mac>().SetPanId(mAlternatePanId);
mAlternatePanId = Mac::kPanIdBroadcast;
mAnnounceHandler.HandleAnnounceAttachFailure();
IgnoreError(BecomeDetached());
}
#if OPENTHREAD_FTD
@@ -2559,7 +2569,7 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
break;
case kCommandAnnounce:
HandleAnnounce(rxInfo);
mAnnounceHandler.HandleAnnounce(rxInfo);
break;
case kCommandChildUpdateRequest:
@@ -3678,7 +3688,7 @@ exit:
LogProcessError(kTypeChildUpdateResponseAsChild, error);
}
void Mle::HandleAnnounce(RxInfo &aRxInfo)
void Mle::AnnounceHandler::HandleAnnounce(RxInfo &aRxInfo)
{
Error error = kErrorNone;
ChannelTlvValue channelTlvValue;
@@ -3711,10 +3721,10 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo)
VerifyOrExit(!channelAndPanIdMatch);
}
SendAnnounce(channel);
Get<Mle>().SendAnnounce(channel);
#if OPENTHREAD_CONFIG_MLE_SEND_UNICAST_ANNOUNCE_RESPONSE
SendAnnounce(channel, aRxInfo.mMessageInfo.GetPeerAddr());
Get<Mle>().SendAnnounce(channel, aRxInfo.mMessageInfo.GetPeerAddr());
#endif
}
else if (timestampCompare > 0)
@@ -3723,7 +3733,7 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo)
// channel and pan-id match the values from the received MLE
// Announce message.
if (IsDetached())
if (Get<Mle>().IsDetached())
{
VerifyOrExit(!channelAndPanIdMatch);
}
@@ -3746,7 +3756,7 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo)
}
}
if (mAttachState == kAttachStateProcessAnnounce)
if (mState == kStateToAnnounceAttach)
{
VerifyOrExit(mAlternateTimestamp < timestamp.GetSeconds());
}
@@ -3754,9 +3764,8 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo)
mAlternateTimestamp = timestamp.GetSeconds();
mAlternateChannel = channel;
mAlternatePanId = panId;
SetAttachState(kAttachStateProcessAnnounce);
mAttachTimer.Start(kAnnounceProcessTimeout);
mAttachCounter = 0;
mState = kStateToAnnounceAttach;
mTimer.Start(kAnnounceProcessTimeout);
LogNote("Delay processing Announce - channel %d, panid 0x%02x", channel, panId);
}
@@ -3854,16 +3863,16 @@ exit:
}
#endif
void Mle::ProcessAnnounce(void)
void Mle::AnnounceHandler::StartAnnounceAttach(void)
{
uint8_t newChannel = mAlternateChannel;
uint16_t newPanId = mAlternatePanId;
OT_ASSERT(mAttachState == kAttachStateProcessAnnounce);
VerifyOrExit(mState == kStateToAnnounceAttach);
LogNote("Processing Announce - channel %d, panid 0x%02x", newChannel, newPanId);
LogNote("Starting Announce attach - channel %d, panid 0x%02x", newChannel, newPanId);
Stop(kKeepNetworkDatasets);
Get<Mle>().Stop(kKeepNetworkDatasets);
// Save the current/previous channel and pan-id
mAlternateChannel = Get<Mac::Mac>().GetPanChannel();
@@ -3873,7 +3882,11 @@ void Mle::ProcessAnnounce(void)
IgnoreError(Get<Mac::Mac>().SetPanChannel(newChannel));
Get<Mac::Mac>().SetPanId(newPanId);
IgnoreError(Start(kAnnounceAttach));
mState = kStateAnnounceAttaching;
IgnoreError(Get<Mle>().Start(kAnnounceAttach));
exit:
return;
}
uint16_t Mle::GetParentRloc16(void) const { return (mParent.IsStateValid() ? mParent.GetRloc16() : kInvalidRloc16); }
@@ -4341,19 +4354,17 @@ const char *Mle::AttachModeToString(AttachMode aMode)
const char *Mle::AttachStateToString(AttachState aState)
{
static const char *const kAttachStateStrings[] = {
"Idle", // (0) kAttachStateIdle
"ProcessAnnounce", // (1) kAttachStateProcessAnnounce
"Start", // (2) kAttachStateStart
"ParentReq", // (3) kAttachStateParent
"Announce", // (4) kAttachStateAnnounce
"ChildIdReq", // (5) kAttachStateChildIdRequest
"Idle", // (0) kAttachStateIdle
"Start", // (1) kAttachStateStart
"ParentReq", // (2) kAttachStateParent
"Announce", // (3) kAttachStateAnnounce
"ChildIdReq", // (4) kAttachStateChildIdRequest
};
struct EnumCheck
{
InitEnumValidatorCounter();
ValidateNextEnum(kAttachStateIdle);
ValidateNextEnum(kAttachStateProcessAnnounce);
ValidateNextEnum(kAttachStateStart);
ValidateNextEnum(kAttachStateParentRequest);
ValidateNextEnum(kAttachStateAnnounce);
@@ -5594,5 +5605,34 @@ void Mle::ParentCandidate::CopyTo(Parent &aParent) const
aParent = *candidateAsParent;
}
//---------------------------------------------------------------------------------------------------------------------
// AnnounceHanlder
Mle::AnnounceHandler::AnnounceHandler(Instance &aInstance)
: InstanceLocator(aInstance)
, mState(kStateIdle)
, mTimer(aInstance)
{
}
void Mle::AnnounceHandler::Stop(void)
{
mTimer.Stop();
mState = kStateIdle;
}
void Mle::AnnounceHandler::HandleAnnounceAttachFailure(void)
{
VerifyOrExit(mState == kStateAnnounceAttaching);
mState = kStateIdle;
IgnoreError(Get<Mac::Mac>().SetPanChannel(mAlternateChannel));
Get<Mac::Mac>().SetPanId(mAlternatePanId);
exit:
return;
}
void Mle::AnnounceHandler::HandleTimer(void) { StartAnnounceAttach(); }
} // namespace Mle
} // namespace ot
+63 -12
View File
@@ -1318,12 +1318,11 @@ private:
enum AttachState : uint8_t
{
kAttachStateIdle, // Not currently searching for a parent.
kAttachStateProcessAnnounce, // Waiting to process a received Announce (to switch channel/pan-id).
kAttachStateStart, // Starting to look for a parent.
kAttachStateParentRequest, // Send Parent Request (current number tracked by `mParentRequestCounter`).
kAttachStateAnnounce, // Send Announce messages
kAttachStateChildIdRequest, // Sending a Child ID Request message.
kAttachStateIdle, // Not currently searching for a parent.
kAttachStateStart, // Starting to look for a parent.
kAttachStateParentRequest, // Send Parent Request (current number tracked by `mParentRequestCounter`).
kAttachStateAnnounce, // Send Announce messages
kAttachStateChildIdRequest, // Sending a Child ID Request message.
};
enum ReattachState : uint8_t
@@ -1733,6 +1732,63 @@ private:
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HandleAnnounceHandlerTimer(void) { mAnnounceHandler.HandleTimer(); }
class AnnounceHandler : public InstanceLocator
{
// Handles received Announce messages with a newer timestamp on
// a different channel and/or PAN ID. It may delay processing
// to collect and handle subsequent Announce messages.
//
// This class also manages an 'announce attach' process, where
// the device tries to attach using the parameters (channel and
// PAN ID) from a processed Announce message.
//
// If the 'announce attach' is successful, this class handles
// sending an Announce on the old channel to inform other
// devices. This is done immediately after attaching as a child
// or after an attempt to transition to the router role is
// complete (whether successful or not).
//
// If the 'announce attach' fails, the class ensures the channel
// and PAN ID are restored to their original values.
public:
explicit AnnounceHandler(Instance &aInstance);
void Stop(void);
void HandleAnnounce(RxInfo &aRxInfo);
bool IsAnnounceAttaching(void) const { return mState == kStateAnnounceAttaching; }
void HandleAnnounceAttachSuccess(void);
void HandleAnnounceAttachFailure(void);
#if OPENTHREAD_FTD
void HandleRouterRoleTransitionAttemptDone(void) { InformPreviousChannel(); }
#endif
void HandleTimer(void);
private:
enum State : uint8_t
{
kStateIdle,
kStateToAnnounceAttach,
kStateAnnounceAttaching,
kStateToInformPreviousChannel,
};
void StartAnnounceAttach(void);
void InformPreviousChannel(void);
using AnnouceTimer = TimerMilliIn<Mle, &Mle::HandleAnnounceHandlerTimer>;
State mState;
uint8_t mAlternateChannel;
uint16_t mAlternatePanId;
uint64_t mAlternateTimestamp;
AnnouceTimer mTimer;
};
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
void HandleParentSearchTimer(void) { mParentSearch.HandleTimer(); }
@@ -1865,7 +1921,6 @@ private:
void SetLeaderData(const LeaderData &aLeaderData);
void SetTimeout(uint32_t aTimeout, TimeoutAction aAction);
void InformPreviousChannel(void);
bool IsAnnounceAttach(void) const { return mAlternatePanId != Mac::kPanIdBroadcast; }
void ScheduleMessageTransmissionTimer(void);
void HandleAttachTimer(void);
void HandleMessageTransmissionTimer(void);
@@ -1878,9 +1933,7 @@ private:
void HandleChildUpdateResponseOnChild(RxInfo &aRxInfo);
void HandleDataResponse(RxInfo &aRxInfo);
void HandleParentResponse(RxInfo &aRxInfo);
void HandleAnnounce(RxInfo &aRxInfo);
Error HandleLeaderData(RxInfo &aRxInfo);
void ProcessAnnounce(void);
bool HasUnregisteredAddress(void);
uint32_t GetAttachStartDelay(void) const;
void SendParentRequest(ParentRequestType aType);
@@ -2084,12 +2137,10 @@ private:
uint8_t mChildUpdateAttempts;
uint8_t mDataRequestAttempts;
uint8_t mAnnounceChannel;
uint8_t mAlternateChannel;
uint16_t mRloc16;
uint16_t mPreviousParentRloc;
uint16_t mAttachCounter;
uint16_t mAnnounceDelay;
uint16_t mAlternatePanId;
uint32_t mStoreFrameCounterAhead;
uint32_t mTimeout;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -2097,7 +2148,6 @@ private:
#endif
uint32_t mLastAttachTime;
uint64_t mLastUpdatedTimestamp;
uint64_t mAlternateTimestamp;
LeaderData mLeaderData;
Parent mParent;
@@ -2107,6 +2157,7 @@ private:
ParentCandidate mParentCandidate;
MleSocket mSocket;
Counters mCounters;
AnnounceHandler mAnnounceHandler;
#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
ParentSearch mParentSearch;
#endif
+3 -3
View File
@@ -343,7 +343,7 @@ void Mle::HandleChildStart(AttachMode aMode)
// decision to become router is determined based on the new
// partition's status.
if (IsAnnounceAttach() && HasChildren())
if (mAnnounceHandler.IsAnnounceAttaching() && HasChildren())
{
RemoveChildren();
}
@@ -1562,7 +1562,7 @@ void Mle::HandleTimeTick(void)
}
else
{
InformPreviousChannel();
mAnnounceHandler.HandleRouterRoleTransitionAttemptDone();
}
if (!mAdvertiseTrickleTimer.IsRunning())
@@ -3460,7 +3460,7 @@ void Mle::HandleAddressSolicitResponse(Coap::Message *aMessage, const Ip6::Messa
}
exit:
InformPreviousChannel();
mAnnounceHandler.HandleRouterRoleTransitionAttemptDone();
}
Error Mle::SetChildRouterLinks(uint8_t aChildRouterLinks)