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/logging.hpp"
40 #include "common/message.hpp"
41 #include "net/ip6.hpp"
42 #include "net/netif.hpp"
43 #include "thread/mesh_forwarder.hpp"
44 #include "thread/mle.hpp"
45 #include "thread/thread_netif.hpp"
46 
47 namespace ot {
48 
DataPollSender(Instance & aInstance)49 DataPollSender::DataPollSender(Instance &aInstance)
50     : InstanceLocator(aInstance)
51     , mTimerStartTime(0)
52     , mPollPeriod(0)
53     , mExternalPollPeriod(0)
54     , mFastPollsUsers(0)
55     , mTimer(aInstance, DataPollSender::HandlePollTimer)
56     , mEnabled(false)
57     , mAttachMode(false)
58     , mRetxMode(false)
59     , mPollTimeoutCounter(0)
60     , mPollTxFailureCounter(0)
61     , mRemainingFastPolls(0)
62 {
63 }
64 
GetParent(void) const65 const Neighbor &DataPollSender::GetParent(void) const
66 {
67     const Neighbor &parentCandidate = Get<Mle::MleRouter>().GetParentCandidate();
68 
69     return parentCandidate.IsStateValid() ? parentCandidate : Get<Mle::MleRouter>().GetParent();
70 }
71 
StartPolling(void)72 void DataPollSender::StartPolling(void)
73 {
74     VerifyOrExit(!mEnabled);
75 
76     OT_ASSERT(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
77 
78     mEnabled = true;
79     ScheduleNextPoll(kRecalculatePollPeriod);
80 
81 exit:
82     return;
83 }
84 
StopPolling(void)85 void DataPollSender::StopPolling(void)
86 {
87     mTimer.Stop();
88     mAttachMode           = false;
89     mRetxMode             = false;
90     mPollTimeoutCounter   = 0;
91     mPollTxFailureCounter = 0;
92     mRemainingFastPolls   = 0;
93     mFastPollsUsers       = 0;
94     mEnabled              = false;
95 }
96 
SendDataPoll(void)97 Error DataPollSender::SendDataPoll(void)
98 {
99     Error error;
100 
101     VerifyOrExit(mEnabled, error = kErrorInvalidState);
102     VerifyOrExit(!Get<Mac::Mac>().GetRxOnWhenIdle(), error = kErrorInvalidState);
103 
104     VerifyOrExit(GetParent().IsStateValidOrRestoring(), error = kErrorInvalidState);
105 
106     mTimer.Stop();
107 
108     SuccessOrExit(error = Get<Mac::Mac>().RequestDataPollTransmission());
109 
110 exit:
111 
112     switch (error)
113     {
114     case kErrorNone:
115         otLogDebgMac("Sending data poll");
116         ScheduleNextPoll(kUsePreviousPollPeriod);
117         break;
118 
119     case kErrorInvalidState:
120         otLogWarnMac("Data poll tx requested while data polling was not enabled!");
121         StopPolling();
122         break;
123 
124     case kErrorAlready:
125         otLogDebgMac("Data poll tx requested when a previous data request still in send queue.");
126         ScheduleNextPoll(kUsePreviousPollPeriod);
127         break;
128 
129     default:
130         otLogWarnMac("Unexpected error %s requesting data poll", ErrorToString(error));
131         ScheduleNextPoll(kRecalculatePollPeriod);
132         break;
133     }
134 
135     return error;
136 }
137 
138 #if OPENTHREAD_CONFIG_MULTI_RADIO
GetPollDestinationAddress(Mac::Address & aDest,Mac::RadioType & aRadioType) const139 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const
140 #else
141 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const
142 #endif
143 {
144     Error           error  = kErrorNone;
145     const Neighbor &parent = GetParent();
146 
147     VerifyOrExit(parent.IsStateValidOrRestoring(), error = kErrorAbort);
148 
149     // Use extended address attaching to a new parent (i.e. parent is the parent candidate).
150     if ((Get<Mac::Mac>().GetShortAddress() == Mac::kShortAddrInvalid) ||
151         (&parent == &Get<Mle::MleRouter>().GetParentCandidate()))
152     {
153         aDest.SetExtended(parent.GetExtAddress());
154     }
155     else
156     {
157         aDest.SetShort(parent.GetRloc16());
158     }
159 
160 #if OPENTHREAD_CONFIG_MULTI_RADIO
161     aRadioType = Get<RadioSelector>().SelectPollFrameRadio(parent);
162 #endif
163 
164 exit:
165     return error;
166 }
167 
SetExternalPollPeriod(uint32_t aPeriod)168 Error DataPollSender::SetExternalPollPeriod(uint32_t aPeriod)
169 {
170     Error error = kErrorNone;
171 
172     if (aPeriod != 0)
173     {
174         VerifyOrExit(aPeriod >= OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD, error = kErrorInvalidArgs);
175 
176         // Clipped by the maximal value.
177         if (aPeriod > kMaxExternalPeriod)
178         {
179             aPeriod = kMaxExternalPeriod;
180         }
181     }
182 
183     if (mExternalPollPeriod != aPeriod)
184     {
185         mExternalPollPeriod = aPeriod;
186 
187         if (mEnabled)
188         {
189             ScheduleNextPoll(kRecalculatePollPeriod);
190         }
191     }
192 
193 exit:
194     return error;
195 }
196 
GetKeepAlivePollPeriod(void) const197 uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const
198 {
199     uint32_t period = GetDefaultPollPeriod();
200 
201     if (mExternalPollPeriod != 0)
202     {
203         period = OT_MIN(period, mExternalPollPeriod);
204     }
205 
206     return period;
207 }
208 
HandlePollSent(Mac::TxFrame & aFrame,Error aError)209 void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError)
210 {
211     Mac::Address macDest;
212     bool         shouldRecalculatePollPeriod = false;
213 
214     VerifyOrExit(mEnabled);
215 
216     if (!aFrame.IsEmpty())
217     {
218         IgnoreError(aFrame.GetDstAddr(macDest));
219         Get<MeshForwarder>().UpdateNeighborOnSentFrame(aFrame, aError, macDest);
220     }
221 
222     if (GetParent().IsStateInvalid())
223     {
224         StopPolling();
225         IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
226         ExitNow();
227     }
228 
229     switch (aError)
230     {
231     case kErrorNone:
232 
233         if (mRemainingFastPolls != 0)
234         {
235             mRemainingFastPolls--;
236 
237             if (mRemainingFastPolls == 0)
238             {
239                 shouldRecalculatePollPeriod = true;
240                 mFastPollsUsers             = 0;
241             }
242         }
243 
244         if (mRetxMode)
245         {
246             mRetxMode                   = false;
247             mPollTxFailureCounter       = 0;
248             shouldRecalculatePollPeriod = true;
249         }
250 
251         break;
252 
253     case kErrorChannelAccessFailure:
254     case kErrorAbort:
255         mRetxMode                   = true;
256         shouldRecalculatePollPeriod = true;
257         break;
258 
259     default:
260         mPollTxFailureCounter++;
261 
262         otLogInfoMac("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
263                      kMaxPollRetxAttempts);
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     otLogInfoMac("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     if (aNumFastPolls > kMaxFastPolls)
414     {
415         aNumFastPolls = kMaxFastPolls;
416     }
417 
418     if (mRemainingFastPolls < aNumFastPolls)
419     {
420         mRemainingFastPolls = aNumFastPolls;
421     }
422 
423     if (mEnabled && shouldRecalculatePollPeriod)
424     {
425         ScheduleNextPoll(kRecalculatePollPeriod);
426     }
427 }
428 
StopFastPolls(void)429 void DataPollSender::StopFastPolls(void)
430 {
431     VerifyOrExit(mFastPollsUsers != 0);
432 
433     // If `mFastPollsUsers` hits the max, let it be cleared
434     // from `HandlePollSent()` (after all fast polls are sent).
435     VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
436 
437     mFastPollsUsers--;
438 
439     VerifyOrExit(mFastPollsUsers == 0);
440 
441     mRemainingFastPolls = 0;
442     ScheduleNextPoll(kRecalculatePollPeriod);
443 
444 exit:
445     return;
446 }
447 
ResetKeepAliveTimer(void)448 void DataPollSender::ResetKeepAliveTimer(void)
449 {
450     if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
451     {
452         mTimerStartTime = TimerMilli::GetNow();
453         mTimer.StartAt(mTimerStartTime, mPollPeriod);
454     }
455 }
456 
ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)457 void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
458 {
459     TimeMilli now;
460     uint32_t  oldPeriod = mPollPeriod;
461 
462     if (aPollPeriodSelector == kRecalculatePollPeriod)
463     {
464         mPollPeriod = CalculatePollPeriod();
465     }
466 
467     now = TimerMilli::GetNow();
468 
469     if (mTimer.IsRunning())
470     {
471         if (oldPeriod != mPollPeriod)
472         {
473             // If poll interval did change and re-starting the timer from
474             // last start time with new poll interval would fire quickly
475             // (i.e., fires within window `[now, now + kMinPollPeriod]`)
476             // add an extra minimum delay of `kMinPollPeriod`. This
477             // ensures that when an internal or external request triggers
478             // a switch to a shorter poll interval, the first data poll
479             // will not be sent too quickly (and possibly before the
480             // response is available/prepared on the parent node).
481 
482             if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
483             {
484                 mTimer.StartAt(now, kMinPollPeriod);
485             }
486             else
487             {
488                 mTimer.StartAt(mTimerStartTime, mPollPeriod);
489             }
490         }
491         // Do nothing on the running poll timer if the poll interval doesn't change
492     }
493     else
494     {
495         mTimerStartTime = now;
496         mTimer.StartAt(mTimerStartTime, mPollPeriod);
497     }
498 }
499 
CalculatePollPeriod(void) const500 uint32_t DataPollSender::CalculatePollPeriod(void) const
501 {
502     uint32_t period = GetDefaultPollPeriod();
503 
504     if (mAttachMode)
505     {
506         period = OT_MIN(period, kAttachDataPollPeriod);
507     }
508 
509     if (mRetxMode)
510     {
511         period = OT_MIN(period, kRetxPollPeriod);
512     }
513 
514     if (mRemainingFastPolls != 0)
515     {
516         period = OT_MIN(period, kFastPollPeriod);
517     }
518 
519     if (mExternalPollPeriod != 0)
520     {
521         period = OT_MIN(period, mExternalPollPeriod);
522     }
523 
524     if (period == 0)
525     {
526         period = kMinPollPeriod;
527     }
528 
529     return period;
530 }
531 
HandlePollTimer(Timer & aTimer)532 void DataPollSender::HandlePollTimer(Timer &aTimer)
533 {
534     IgnoreError(aTimer.Get<DataPollSender>().SendDataPoll());
535 }
536 
GetDefaultPollPeriod(void) const537 uint32_t DataPollSender::GetDefaultPollPeriod(void) const
538 {
539     uint32_t period    = Time::SecToMsec(Get<Mle::MleRouter>().GetTimeout());
540     uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
541 
542 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
543     if (Get<Mac::Mac>().IsCslEnabled())
544     {
545         period = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
546     }
547 #endif
548 
549     if (period > pollAhead)
550     {
551         period -= pollAhead;
552     }
553 
554     return period;
555 }
556 
PrepareDataRequest(Mac::TxFrames & aTxFrames)557 Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
558 {
559     Mac::TxFrame *frame = nullptr;
560     Mac::Address  src, dst;
561     uint16_t      fcf;
562     bool          iePresent;
563 
564 #if OPENTHREAD_CONFIG_MULTI_RADIO
565     Mac::RadioType radio;
566 
567     SuccessOrExit(GetPollDestinationAddress(dst, radio));
568     frame = &aTxFrames.GetTxFrame(radio);
569 #else
570     SuccessOrExit(GetPollDestinationAddress(dst));
571     frame = &aTxFrames.GetTxFrame();
572 #endif
573 
574     fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfAckRequest |
575           Mac::Frame::kFcfSecurityEnabled;
576 
577     iePresent = Get<MeshForwarder>().CalcIePresent(nullptr);
578 
579     if (iePresent)
580     {
581         fcf |= Mac::Frame::kFcfIePresent;
582     }
583 
584     fcf |= Get<MeshForwarder>().CalcFrameVersion(Get<NeighborTable>().FindNeighbor(dst), iePresent);
585 
586     if (dst.IsExtended())
587     {
588         fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt;
589         src.SetExtended(Get<Mac::Mac>().GetExtAddress());
590     }
591     else
592     {
593         fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort;
594         src.SetShort(Get<Mac::Mac>().GetShortAddress());
595     }
596 
597     frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
598 
599     if (frame->IsDstPanIdPresent())
600     {
601         frame->SetDstPanId(Get<Mac::Mac>().GetPanId());
602     }
603 
604     frame->SetSrcAddr(src);
605     frame->SetDstAddr(dst);
606 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
607     if (iePresent)
608     {
609         Get<MeshForwarder>().AppendHeaderIe(nullptr, *frame);
610     }
611 
612 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
613     if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)
614     {
615         // Disable frame retransmission when the data poll has CSL IE included
616         aTxFrames.SetMaxFrameRetries(0);
617     }
618 #endif
619 #endif
620 
621     IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest));
622 
623 exit:
624     return frame;
625 }
626 
627 } // namespace ot
628