1 /*
2  *  Copyright (c) 2017, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements data poll (mac data request command) sender class.
32  */
33 
34 #include "data_poll_sender.hpp"
35 
36 #include "common/code_utils.hpp"
37 #include "common/locator_getters.hpp"
38 #include "common/log.hpp"
39 #include "common/message.hpp"
40 #include "common/num_utils.hpp"
41 #include "instance/instance.hpp"
42 #include "net/ip6.hpp"
43 #include "net/netif.hpp"
44 #include "thread/mesh_forwarder.hpp"
45 #include "thread/mle.hpp"
46 #include "thread/thread_netif.hpp"
47 
48 namespace ot {
49 
50 RegisterLogModule("DataPollSender");
51 
DataPollSender(Instance & aInstance)52 DataPollSender::DataPollSender(Instance &aInstance)
53     : InstanceLocator(aInstance)
54     , mTimerStartTime(0)
55     , mPollPeriod(0)
56     , mExternalPollPeriod(0)
57     , mFastPollsUsers(0)
58     , mTimer(aInstance)
59     , mEnabled(false)
60     , mAttachMode(false)
61     , mRetxMode(false)
62     , mPollTimeoutCounter(0)
63     , mPollTxFailureCounter(0)
64     , mRemainingFastPolls(0)
65 {
66 }
67 
GetParent(void) const68 const Neighbor &DataPollSender::GetParent(void) const
69 {
70     const Neighbor &parentCandidate = Get<Mle::MleRouter>().GetParentCandidate();
71 
72     return parentCandidate.IsStateValid() ? parentCandidate : Get<Mle::MleRouter>().GetParent();
73 }
74 
StartPolling(void)75 void DataPollSender::StartPolling(void)
76 {
77     VerifyOrExit(!mEnabled);
78 
79     OT_ASSERT(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
80 
81     mEnabled = true;
82     ScheduleNextPoll(kRecalculatePollPeriod);
83 
84 exit:
85     return;
86 }
87 
StopPolling(void)88 void DataPollSender::StopPolling(void)
89 {
90     mTimer.Stop();
91     mAttachMode           = false;
92     mRetxMode             = false;
93     mPollTimeoutCounter   = 0;
94     mPollTxFailureCounter = 0;
95     mRemainingFastPolls   = 0;
96     mFastPollsUsers       = 0;
97     mEnabled              = false;
98 }
99 
SendDataPoll(void)100 Error DataPollSender::SendDataPoll(void)
101 {
102     Error error;
103 
104     VerifyOrExit(mEnabled, error = kErrorInvalidState);
105     VerifyOrExit(!Get<Mac::Mac>().GetRxOnWhenIdle(), error = kErrorInvalidState);
106 
107     VerifyOrExit(GetParent().IsStateValidOrRestoring(), error = kErrorInvalidState);
108 
109     mTimer.Stop();
110 
111     SuccessOrExit(error = Get<Mac::Mac>().RequestDataPollTransmission());
112 
113 exit:
114 
115     switch (error)
116     {
117     case kErrorNone:
118         LogDebg("Sending data poll");
119         ScheduleNextPoll(kUsePreviousPollPeriod);
120         break;
121 
122     case kErrorInvalidState:
123         LogWarn("Data poll tx requested while data polling was not enabled!");
124         StopPolling();
125         break;
126 
127     default:
128         LogWarn("Unexpected error %s requesting data poll", ErrorToString(error));
129         ScheduleNextPoll(kRecalculatePollPeriod);
130         break;
131     }
132 
133     return error;
134 }
135 
136 #if OPENTHREAD_CONFIG_MULTI_RADIO
GetPollDestinationAddress(Mac::Address & aDest,Mac::RadioType & aRadioType) const137 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const
138 #else
139 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const
140 #endif
141 {
142     Error           error  = kErrorNone;
143     const Neighbor &parent = GetParent();
144 
145     VerifyOrExit(parent.IsStateValidOrRestoring(), error = kErrorAbort);
146 
147     // Use extended address attaching to a new parent (i.e. parent is the parent candidate).
148     if ((Get<Mac::Mac>().GetShortAddress() == Mac::kShortAddrInvalid) ||
149         (&parent == &Get<Mle::MleRouter>().GetParentCandidate()))
150     {
151         aDest.SetExtended(parent.GetExtAddress());
152     }
153     else
154     {
155         aDest.SetShort(parent.GetRloc16());
156     }
157 
158 #if OPENTHREAD_CONFIG_MULTI_RADIO
159     aRadioType = Get<RadioSelector>().SelectPollFrameRadio(parent);
160 #endif
161 
162 exit:
163     return error;
164 }
165 
SetExternalPollPeriod(uint32_t aPeriod)166 Error DataPollSender::SetExternalPollPeriod(uint32_t aPeriod)
167 {
168     Error error = kErrorNone;
169 
170     if (aPeriod != 0)
171     {
172         VerifyOrExit(aPeriod >= OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD, error = kErrorInvalidArgs);
173 
174         aPeriod = Min(aPeriod, kMaxExternalPeriod);
175     }
176 
177     if (mExternalPollPeriod != aPeriod)
178     {
179         mExternalPollPeriod = aPeriod;
180 
181         if (mEnabled)
182         {
183             ScheduleNextPoll(kRecalculatePollPeriod);
184         }
185     }
186 
187 exit:
188     return error;
189 }
190 
GetKeepAlivePollPeriod(void) const191 uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const
192 {
193     uint32_t period = GetDefaultPollPeriod();
194 
195     if (mExternalPollPeriod != 0)
196     {
197         period = Min(period, mExternalPollPeriod);
198     }
199 
200     return period;
201 }
202 
HandlePollSent(Mac::TxFrame & aFrame,Error aError)203 void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError)
204 {
205     Mac::Address macDest;
206     bool         shouldRecalculatePollPeriod = false;
207 
208     VerifyOrExit(mEnabled);
209 
210     if (!aFrame.IsEmpty())
211     {
212         IgnoreError(aFrame.GetDstAddr(macDest));
213         Get<MeshForwarder>().UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ true);
214     }
215 
216     if (GetParent().IsStateInvalid())
217     {
218         StopPolling();
219         IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
220         ExitNow();
221     }
222 
223     switch (aError)
224     {
225     case kErrorNone:
226 
227         if (mRemainingFastPolls != 0)
228         {
229             mRemainingFastPolls--;
230 
231             if (mRemainingFastPolls == 0)
232             {
233                 shouldRecalculatePollPeriod = true;
234                 mFastPollsUsers             = 0;
235             }
236         }
237 
238         if (mRetxMode)
239         {
240             mRetxMode                   = false;
241             mPollTxFailureCounter       = 0;
242             shouldRecalculatePollPeriod = true;
243         }
244 
245         break;
246 
247     case kErrorChannelAccessFailure:
248     case kErrorAbort:
249         mRetxMode                   = true;
250         shouldRecalculatePollPeriod = true;
251         break;
252 
253     default:
254         mPollTxFailureCounter++;
255 
256 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
257         LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
258                 aFrame.HasCslIe() ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts);
259 #else
260         LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
261                 kMaxPollRetxAttempts);
262 #endif
263 
264 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
265         if (mPollTxFailureCounter < (aFrame.HasCslIe() ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts))
266 #else
267         if (mPollTxFailureCounter < kMaxPollRetxAttempts)
268 #endif
269         {
270             if (!mRetxMode)
271             {
272                 mRetxMode                   = true;
273                 shouldRecalculatePollPeriod = true;
274             }
275         }
276         else
277         {
278             mRetxMode                   = false;
279             mPollTxFailureCounter       = 0;
280             shouldRecalculatePollPeriod = true;
281         }
282 
283         break;
284     }
285 
286     if (shouldRecalculatePollPeriod)
287     {
288         ScheduleNextPoll(kRecalculatePollPeriod);
289     }
290 
291 exit:
292     return;
293 }
294 
HandlePollTimeout(void)295 void DataPollSender::HandlePollTimeout(void)
296 {
297     // A data poll timeout happened, i.e., the ack in response to
298     // a data poll indicated that a frame was pending, but no frame
299     // was received after timeout interval.
300 
301     VerifyOrExit(mEnabled);
302 
303     mPollTimeoutCounter++;
304 
305     LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
306 
307     if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
308     {
309         IgnoreError(SendDataPoll());
310     }
311     else
312     {
313         mPollTimeoutCounter = 0;
314     }
315 
316 exit:
317     return;
318 }
319 
ProcessRxFrame(const Mac::RxFrame & aFrame)320 void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame)
321 {
322     VerifyOrExit(mEnabled);
323 
324     mPollTimeoutCounter = 0;
325 
326     if (aFrame.GetFramePending())
327     {
328         IgnoreError(SendDataPoll());
329     }
330 
331 exit:
332     return;
333 }
334 
335 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
ProcessTxDone(const Mac::TxFrame & aFrame,const Mac::RxFrame * aAckFrame,Error aError)336 void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError)
337 {
338     bool sendDataPoll = false;
339 
340     VerifyOrExit(mEnabled);
341     VerifyOrExit(Get<Mle::MleRouter>().GetParent().IsEnhancedKeepAliveSupported());
342     VerifyOrExit(aFrame.GetSecurityEnabled());
343 
344 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
345     if (aFrame.mInfo.mTxInfo.mIsARetx && aFrame.HasCslIe())
346     {
347         // For retransmission frame, use a data poll to resync its parent with correct CSL phase
348         sendDataPoll = true;
349     }
350 #endif
351 
352     if (aError == kErrorNone && aAckFrame != nullptr)
353     {
354         mPollTimeoutCounter = 0;
355 
356         if (aAckFrame->GetFramePending())
357         {
358             sendDataPoll = true;
359         }
360         else
361         {
362             ResetKeepAliveTimer();
363         }
364     }
365 
366     if (sendDataPoll)
367     {
368         IgnoreError(SendDataPoll());
369     }
370 
371 exit:
372     return;
373 }
374 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
375 
RecalculatePollPeriod(void)376 void DataPollSender::RecalculatePollPeriod(void)
377 {
378     if (mEnabled)
379     {
380         ScheduleNextPoll(kRecalculatePollPeriod);
381     }
382 }
383 
SetAttachMode(bool aMode)384 void DataPollSender::SetAttachMode(bool aMode)
385 {
386     if (mAttachMode != aMode)
387     {
388         mAttachMode = aMode;
389 
390         if (mEnabled)
391         {
392             ScheduleNextPoll(kRecalculatePollPeriod);
393         }
394     }
395 }
396 
SendFastPolls(uint8_t aNumFastPolls)397 void DataPollSender::SendFastPolls(uint8_t aNumFastPolls)
398 {
399     bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0);
400 
401     if (mFastPollsUsers < kMaxFastPollsUsers)
402     {
403         mFastPollsUsers++;
404     }
405 
406     if (aNumFastPolls == 0)
407     {
408         aNumFastPolls = kDefaultFastPolls;
409     }
410 
411     aNumFastPolls       = Min(aNumFastPolls, kMaxFastPolls);
412     mRemainingFastPolls = Max(mRemainingFastPolls, aNumFastPolls);
413 
414     if (mEnabled && shouldRecalculatePollPeriod)
415     {
416         ScheduleNextPoll(kRecalculatePollPeriod);
417     }
418 }
419 
StopFastPolls(void)420 void DataPollSender::StopFastPolls(void)
421 {
422     VerifyOrExit(mFastPollsUsers != 0);
423 
424     // If `mFastPollsUsers` hits the max, let it be cleared
425     // from `HandlePollSent()` (after all fast polls are sent).
426     VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
427 
428     mFastPollsUsers--;
429 
430     VerifyOrExit(mFastPollsUsers == 0);
431 
432     mRemainingFastPolls = 0;
433     ScheduleNextPoll(kRecalculatePollPeriod);
434 
435 exit:
436     return;
437 }
438 
ResetKeepAliveTimer(void)439 void DataPollSender::ResetKeepAliveTimer(void)
440 {
441     if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
442     {
443         mTimerStartTime = TimerMilli::GetNow();
444         mTimer.StartAt(mTimerStartTime, mPollPeriod);
445     }
446 }
447 
ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)448 void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
449 {
450     TimeMilli now;
451     uint32_t  oldPeriod = mPollPeriod;
452 
453     if (aPollPeriodSelector == kRecalculatePollPeriod)
454     {
455         mPollPeriod = CalculatePollPeriod();
456     }
457 
458     now = TimerMilli::GetNow();
459 
460     if (mTimer.IsRunning())
461     {
462         if (oldPeriod != mPollPeriod)
463         {
464             // If poll interval did change and re-starting the timer from
465             // last start time with new poll interval would fire quickly
466             // (i.e., fires within window `[now, now + kMinPollPeriod]`)
467             // add an extra minimum delay of `kMinPollPeriod`. This
468             // ensures that when an internal or external request triggers
469             // a switch to a shorter poll interval, the first data poll
470             // will not be sent too quickly (and possibly before the
471             // response is available/prepared on the parent node).
472 
473             if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
474             {
475                 mTimer.StartAt(now, kMinPollPeriod);
476             }
477             else
478             {
479                 mTimer.StartAt(mTimerStartTime, mPollPeriod);
480             }
481         }
482         // Do nothing on the running poll timer if the poll interval doesn't change
483     }
484     else
485     {
486         mTimerStartTime = now;
487         mTimer.StartAt(mTimerStartTime, mPollPeriod);
488     }
489 }
490 
CalculatePollPeriod(void) const491 uint32_t DataPollSender::CalculatePollPeriod(void) const
492 {
493     uint32_t period = GetDefaultPollPeriod();
494 
495     if (mAttachMode)
496     {
497         period = Min(period, kAttachDataPollPeriod);
498     }
499 
500     if (mRetxMode)
501     {
502         period = Min(period, kRetxPollPeriod);
503 
504 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
505         if (Get<Mac::Mac>().GetCslPeriodInMsec() > 0)
506         {
507             period = Min(period, Get<Mac::Mac>().GetCslPeriodInMsec());
508         }
509 #endif
510     }
511 
512     if (mRemainingFastPolls != 0)
513     {
514         period = Min(period, kFastPollPeriod);
515     }
516 
517     if (mExternalPollPeriod != 0)
518     {
519         period = Min(period, mExternalPollPeriod);
520     }
521 
522     if (period == 0)
523     {
524         period = kMinPollPeriod;
525     }
526 
527     return period;
528 }
529 
GetDefaultPollPeriod(void) const530 uint32_t DataPollSender::GetDefaultPollPeriod(void) const
531 {
532     uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
533     uint32_t period;
534 
535     period = Time::SecToMsec(Min(Get<Mle::MleRouter>().GetTimeout(), Time::MsecToSec(TimerMilli::kMaxDelay)));
536 
537 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
538     if (Get<Mac::Mac>().IsCslEnabled())
539     {
540         period    = Min(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
541         pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
542     }
543 #endif
544 
545     if (period > pollAhead)
546     {
547         period -= pollAhead;
548     }
549 
550     return period;
551 }
552 
PrepareDataRequest(Mac::TxFrames & aTxFrames)553 Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
554 {
555     Mac::TxFrame  *frame = nullptr;
556     Mac::Addresses addresses;
557     Mac::PanIds    panIds;
558 
559 #if OPENTHREAD_CONFIG_MULTI_RADIO
560     Mac::RadioType radio;
561 
562     SuccessOrExit(GetPollDestinationAddress(addresses.mDestination, radio));
563     frame = &aTxFrames.GetTxFrame(radio);
564 #else
565     SuccessOrExit(GetPollDestinationAddress(addresses.mDestination));
566     frame = &aTxFrames.GetTxFrame();
567 #endif
568 
569     if (addresses.mDestination.IsExtended())
570     {
571         addresses.mSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
572     }
573     else
574     {
575         addresses.mSource.SetShort(Get<Mac::Mac>().GetShortAddress());
576     }
577 
578     panIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
579 
580     Get<MeshForwarder>().PrepareMacHeaders(*frame, Mac::Frame::kTypeMacCmd, addresses, panIds,
581                                            Mac::Frame::kSecurityEncMic32, Mac::Frame::kKeyIdMode1, nullptr);
582 
583 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
584     if (frame->HasCslIe())
585     {
586         // Disable frame retransmission when the data poll has CSL IE included
587         aTxFrames.SetMaxFrameRetries(0);
588     }
589 #endif
590 
591     IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest));
592 
593 exit:
594     return frame;
595 }
596 
597 } // namespace ot
598