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/instance.hpp"
38 #include "common/locator_getters.hpp"
39 #include "common/log.hpp"
40 #include "common/message.hpp"
41 #include "common/num_utils.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.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts
259                                                                          : kMaxPollRetxAttempts);
260 #else
261         LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
262                 kMaxPollRetxAttempts);
263 #endif
264 
265 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
266         if (mPollTxFailureCounter <
267             ((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts))
268 #else
269         if (mPollTxFailureCounter < kMaxPollRetxAttempts)
270 #endif
271         {
272             if (!mRetxMode)
273             {
274                 mRetxMode                   = true;
275                 shouldRecalculatePollPeriod = true;
276             }
277         }
278         else
279         {
280             mRetxMode                   = false;
281             mPollTxFailureCounter       = 0;
282             shouldRecalculatePollPeriod = true;
283         }
284 
285         break;
286     }
287 
288     if (shouldRecalculatePollPeriod)
289     {
290         ScheduleNextPoll(kRecalculatePollPeriod);
291     }
292 
293 exit:
294     return;
295 }
296 
HandlePollTimeout(void)297 void DataPollSender::HandlePollTimeout(void)
298 {
299     // A data poll timeout happened, i.e., the ack in response to
300     // a data poll indicated that a frame was pending, but no frame
301     // was received after timeout interval.
302 
303     VerifyOrExit(mEnabled);
304 
305     mPollTimeoutCounter++;
306 
307     LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
308 
309     if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
310     {
311         IgnoreError(SendDataPoll());
312     }
313     else
314     {
315         mPollTimeoutCounter = 0;
316     }
317 
318 exit:
319     return;
320 }
321 
ProcessRxFrame(const Mac::RxFrame & aFrame)322 void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame)
323 {
324     VerifyOrExit(mEnabled);
325 
326     mPollTimeoutCounter = 0;
327 
328     if (aFrame.GetFramePending())
329     {
330         IgnoreError(SendDataPoll());
331     }
332 
333 exit:
334     return;
335 }
336 
337 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
ProcessTxDone(const Mac::TxFrame & aFrame,const Mac::RxFrame * aAckFrame,Error aError)338 void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError)
339 {
340     bool sendDataPoll = false;
341 
342     VerifyOrExit(mEnabled);
343     VerifyOrExit(Get<Mle::MleRouter>().GetParent().IsEnhancedKeepAliveSupported());
344     VerifyOrExit(aFrame.GetSecurityEnabled());
345 
346 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
347     if (aFrame.mInfo.mTxInfo.mIsARetx && (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr))
348     {
349         // For retransmission frame, use a data poll to resync its parent with correct CSL phase
350         sendDataPoll = true;
351     }
352 #endif
353 
354     if (aError == kErrorNone && aAckFrame != nullptr)
355     {
356         mPollTimeoutCounter = 0;
357 
358         if (aAckFrame->GetFramePending())
359         {
360             sendDataPoll = true;
361         }
362         else
363         {
364             ResetKeepAliveTimer();
365         }
366     }
367 
368     if (sendDataPoll)
369     {
370         IgnoreError(SendDataPoll());
371     }
372 
373 exit:
374     return;
375 }
376 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
377 
RecalculatePollPeriod(void)378 void DataPollSender::RecalculatePollPeriod(void)
379 {
380     if (mEnabled)
381     {
382         ScheduleNextPoll(kRecalculatePollPeriod);
383     }
384 }
385 
SetAttachMode(bool aMode)386 void DataPollSender::SetAttachMode(bool aMode)
387 {
388     if (mAttachMode != aMode)
389     {
390         mAttachMode = aMode;
391 
392         if (mEnabled)
393         {
394             ScheduleNextPoll(kRecalculatePollPeriod);
395         }
396     }
397 }
398 
SendFastPolls(uint8_t aNumFastPolls)399 void DataPollSender::SendFastPolls(uint8_t aNumFastPolls)
400 {
401     bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0);
402 
403     if (mFastPollsUsers < kMaxFastPollsUsers)
404     {
405         mFastPollsUsers++;
406     }
407 
408     if (aNumFastPolls == 0)
409     {
410         aNumFastPolls = kDefaultFastPolls;
411     }
412 
413     aNumFastPolls       = Min(aNumFastPolls, kMaxFastPolls);
414     mRemainingFastPolls = Max(mRemainingFastPolls, aNumFastPolls);
415 
416     if (mEnabled && shouldRecalculatePollPeriod)
417     {
418         ScheduleNextPoll(kRecalculatePollPeriod);
419     }
420 }
421 
StopFastPolls(void)422 void DataPollSender::StopFastPolls(void)
423 {
424     VerifyOrExit(mFastPollsUsers != 0);
425 
426     // If `mFastPollsUsers` hits the max, let it be cleared
427     // from `HandlePollSent()` (after all fast polls are sent).
428     VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
429 
430     mFastPollsUsers--;
431 
432     VerifyOrExit(mFastPollsUsers == 0);
433 
434     mRemainingFastPolls = 0;
435     ScheduleNextPoll(kRecalculatePollPeriod);
436 
437 exit:
438     return;
439 }
440 
ResetKeepAliveTimer(void)441 void DataPollSender::ResetKeepAliveTimer(void)
442 {
443     if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
444     {
445         mTimerStartTime = TimerMilli::GetNow();
446         mTimer.StartAt(mTimerStartTime, mPollPeriod);
447     }
448 }
449 
ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)450 void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
451 {
452     TimeMilli now;
453     uint32_t  oldPeriod = mPollPeriod;
454 
455     if (aPollPeriodSelector == kRecalculatePollPeriod)
456     {
457         mPollPeriod = CalculatePollPeriod();
458     }
459 
460     now = TimerMilli::GetNow();
461 
462     if (mTimer.IsRunning())
463     {
464         if (oldPeriod != mPollPeriod)
465         {
466             // If poll interval did change and re-starting the timer from
467             // last start time with new poll interval would fire quickly
468             // (i.e., fires within window `[now, now + kMinPollPeriod]`)
469             // add an extra minimum delay of `kMinPollPeriod`. This
470             // ensures that when an internal or external request triggers
471             // a switch to a shorter poll interval, the first data poll
472             // will not be sent too quickly (and possibly before the
473             // response is available/prepared on the parent node).
474 
475             if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
476             {
477                 mTimer.StartAt(now, kMinPollPeriod);
478             }
479             else
480             {
481                 mTimer.StartAt(mTimerStartTime, mPollPeriod);
482             }
483         }
484         // Do nothing on the running poll timer if the poll interval doesn't change
485     }
486     else
487     {
488         mTimerStartTime = now;
489         mTimer.StartAt(mTimerStartTime, mPollPeriod);
490     }
491 }
492 
CalculatePollPeriod(void) const493 uint32_t DataPollSender::CalculatePollPeriod(void) const
494 {
495     uint32_t period = GetDefaultPollPeriod();
496 
497     if (mAttachMode)
498     {
499         period = Min(period, kAttachDataPollPeriod);
500     }
501 
502     if (mRetxMode)
503     {
504         period = Min(period, kRetxPollPeriod);
505 
506 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
507         if (Get<Mac::Mac>().GetCslPeriodInMsec() > 0)
508         {
509             period = Min(period, Get<Mac::Mac>().GetCslPeriodInMsec());
510         }
511 #endif
512     }
513 
514     if (mRemainingFastPolls != 0)
515     {
516         period = Min(period, kFastPollPeriod);
517     }
518 
519     if (mExternalPollPeriod != 0)
520     {
521         period = Min(period, mExternalPollPeriod);
522     }
523 
524     if (period == 0)
525     {
526         period = kMinPollPeriod;
527     }
528 
529     return period;
530 }
531 
GetDefaultPollPeriod(void) const532 uint32_t DataPollSender::GetDefaultPollPeriod(void) const
533 {
534     uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
535     uint32_t period;
536 
537     period = Time::SecToMsec(Min(Get<Mle::MleRouter>().GetTimeout(), Time::MsecToSec(TimerMilli::kMaxDelay)));
538 
539 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
540     if (Get<Mac::Mac>().IsCslEnabled())
541     {
542         period    = Min(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
543         pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
544     }
545 #endif
546 
547     if (period > pollAhead)
548     {
549         period -= pollAhead;
550     }
551 
552     return period;
553 }
554 
PrepareDataRequest(Mac::TxFrames & aTxFrames)555 Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
556 {
557     Mac::TxFrame  *frame = nullptr;
558     Mac::Addresses addresses;
559     Mac::PanIds    panIds;
560 
561 #if OPENTHREAD_CONFIG_MULTI_RADIO
562     Mac::RadioType radio;
563 
564     SuccessOrExit(GetPollDestinationAddress(addresses.mDestination, radio));
565     frame = &aTxFrames.GetTxFrame(radio);
566 #else
567     SuccessOrExit(GetPollDestinationAddress(addresses.mDestination));
568     frame = &aTxFrames.GetTxFrame();
569 #endif
570 
571     if (addresses.mDestination.IsExtended())
572     {
573         addresses.mSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
574     }
575     else
576     {
577         addresses.mSource.SetShort(Get<Mac::Mac>().GetShortAddress());
578     }
579 
580     panIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
581 
582     Get<MeshForwarder>().PrepareMacHeaders(*frame, Mac::Frame::kTypeMacCmd, addresses, panIds,
583                                            Mac::Frame::kSecurityEncMic32, Mac::Frame::kKeyIdMode1, nullptr);
584 
585 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
586     if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)
587     {
588         // Disable frame retransmission when the data poll has CSL IE included
589         aTxFrames.SetMaxFrameRetries(0);
590     }
591 #endif
592 
593     IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest));
594 
595 exit:
596     return frame;
597 }
598 
599 } // namespace ot
600