• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
49 RegisterLogModule("DataPollSender");
50 
DataPollSender(Instance & aInstance)51 DataPollSender::DataPollSender(Instance &aInstance)
52     : InstanceLocator(aInstance)
53     , mTimerStartTime(0)
54     , mPollPeriod(0)
55     , mExternalPollPeriod(0)
56     , mFastPollsUsers(0)
57     , mTimer(aInstance, DataPollSender::HandlePollTimer)
58     , mEnabled(false)
59     , mAttachMode(false)
60     , mRetxMode(false)
61     , mPollTimeoutCounter(0)
62     , mPollTxFailureCounter(0)
63     , mRemainingFastPolls(0)
64 {
65 }
66 
GetParent(void) const67 const Neighbor &DataPollSender::GetParent(void) const
68 {
69     const Neighbor &parentCandidate = Get<Mle::MleRouter>().GetParentCandidate();
70 
71     return parentCandidate.IsStateValid() ? parentCandidate : Get<Mle::MleRouter>().GetParent();
72 }
73 
StartPolling(void)74 void DataPollSender::StartPolling(void)
75 {
76     VerifyOrExit(!mEnabled);
77 
78     OT_ASSERT(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
79 
80     mEnabled = true;
81     ScheduleNextPoll(kRecalculatePollPeriod);
82 
83 exit:
84     return;
85 }
86 
StopPolling(void)87 void DataPollSender::StopPolling(void)
88 {
89     mTimer.Stop();
90     mAttachMode           = false;
91     mRetxMode             = false;
92     mPollTimeoutCounter   = 0;
93     mPollTxFailureCounter = 0;
94     mRemainingFastPolls   = 0;
95     mFastPollsUsers       = 0;
96     mEnabled              = false;
97 }
98 
SendDataPoll(void)99 Error DataPollSender::SendDataPoll(void)
100 {
101     Error error;
102 
103     VerifyOrExit(mEnabled, error = kErrorInvalidState);
104     VerifyOrExit(!Get<Mac::Mac>().GetRxOnWhenIdle(), error = kErrorInvalidState);
105 
106     VerifyOrExit(GetParent().IsStateValidOrRestoring(), error = kErrorInvalidState);
107 
108     mTimer.Stop();
109 
110     SuccessOrExit(error = Get<Mac::Mac>().RequestDataPollTransmission());
111 
112 exit:
113 
114     switch (error)
115     {
116     case kErrorNone:
117         LogDebg("Sending data poll");
118         ScheduleNextPoll(kUsePreviousPollPeriod);
119         break;
120 
121     case kErrorInvalidState:
122         LogWarn("Data poll tx requested while data polling was not enabled!");
123         StopPolling();
124         break;
125 
126     default:
127         LogWarn("Unexpected error %s requesting data poll", ErrorToString(error));
128         ScheduleNextPoll(kRecalculatePollPeriod);
129         break;
130     }
131 
132     return error;
133 }
134 
135 #if OPENTHREAD_CONFIG_MULTI_RADIO
GetPollDestinationAddress(Mac::Address & aDest,Mac::RadioType & aRadioType) const136 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const
137 #else
138 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const
139 #endif
140 {
141     Error           error  = kErrorNone;
142     const Neighbor &parent = GetParent();
143 
144     VerifyOrExit(parent.IsStateValidOrRestoring(), error = kErrorAbort);
145 
146     // Use extended address attaching to a new parent (i.e. parent is the parent candidate).
147     if ((Get<Mac::Mac>().GetShortAddress() == Mac::kShortAddrInvalid) ||
148         (&parent == &Get<Mle::MleRouter>().GetParentCandidate()))
149     {
150         aDest.SetExtended(parent.GetExtAddress());
151     }
152     else
153     {
154         aDest.SetShort(parent.GetRloc16());
155     }
156 
157 #if OPENTHREAD_CONFIG_MULTI_RADIO
158     aRadioType = Get<RadioSelector>().SelectPollFrameRadio(parent);
159 #endif
160 
161 exit:
162     return error;
163 }
164 
SetExternalPollPeriod(uint32_t aPeriod)165 Error DataPollSender::SetExternalPollPeriod(uint32_t aPeriod)
166 {
167     Error error = kErrorNone;
168 
169     if (aPeriod != 0)
170     {
171         VerifyOrExit(aPeriod >= OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD, error = kErrorInvalidArgs);
172 
173         // Clipped by the maximal value.
174         if (aPeriod > kMaxExternalPeriod)
175         {
176             aPeriod = kMaxExternalPeriod;
177         }
178     }
179 
180     if (mExternalPollPeriod != aPeriod)
181     {
182         mExternalPollPeriod = aPeriod;
183 
184         if (mEnabled)
185         {
186             ScheduleNextPoll(kRecalculatePollPeriod);
187         }
188     }
189 
190 exit:
191     return error;
192 }
193 
GetKeepAlivePollPeriod(void) const194 uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const
195 {
196     uint32_t period = GetDefaultPollPeriod();
197 
198     if (mExternalPollPeriod != 0)
199     {
200         period = OT_MIN(period, mExternalPollPeriod);
201     }
202 
203     return period;
204 }
205 
HandlePollSent(Mac::TxFrame & aFrame,Error aError)206 void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError)
207 {
208     Mac::Address macDest;
209     bool         shouldRecalculatePollPeriod = false;
210 
211     VerifyOrExit(mEnabled);
212 
213     if (!aFrame.IsEmpty())
214     {
215         IgnoreError(aFrame.GetDstAddr(macDest));
216         Get<MeshForwarder>().UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ true);
217     }
218 
219     if (GetParent().IsStateInvalid())
220     {
221         StopPolling();
222         IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
223         ExitNow();
224     }
225 
226     switch (aError)
227     {
228     case kErrorNone:
229 
230         if (mRemainingFastPolls != 0)
231         {
232             mRemainingFastPolls--;
233 
234             if (mRemainingFastPolls == 0)
235             {
236                 shouldRecalculatePollPeriod = true;
237                 mFastPollsUsers             = 0;
238             }
239         }
240 
241         if (mRetxMode)
242         {
243             mRetxMode                   = false;
244             mPollTxFailureCounter       = 0;
245             shouldRecalculatePollPeriod = true;
246         }
247 
248         break;
249 
250     case kErrorChannelAccessFailure:
251     case kErrorAbort:
252         mRetxMode                   = true;
253         shouldRecalculatePollPeriod = true;
254         break;
255 
256     default:
257         mPollTxFailureCounter++;
258 
259 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
260         LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
261                 (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts
262                                                                          : kMaxPollRetxAttempts);
263 #else
264         LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
265                 kMaxPollRetxAttempts);
266 #endif
267 
268 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
269         if (mPollTxFailureCounter <
270             ((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts))
271 #else
272         if (mPollTxFailureCounter < kMaxPollRetxAttempts)
273 #endif
274         {
275             if (!mRetxMode)
276             {
277                 mRetxMode                   = true;
278                 shouldRecalculatePollPeriod = true;
279             }
280         }
281         else
282         {
283             mRetxMode                   = false;
284             mPollTxFailureCounter       = 0;
285             shouldRecalculatePollPeriod = true;
286         }
287 
288         break;
289     }
290 
291     if (shouldRecalculatePollPeriod)
292     {
293         ScheduleNextPoll(kRecalculatePollPeriod);
294     }
295 
296 exit:
297     return;
298 }
299 
HandlePollTimeout(void)300 void DataPollSender::HandlePollTimeout(void)
301 {
302     // A data poll timeout happened, i.e., the ack in response to
303     // a data poll indicated that a frame was pending, but no frame
304     // was received after timeout interval.
305 
306     VerifyOrExit(mEnabled);
307 
308     mPollTimeoutCounter++;
309 
310     LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
311 
312     if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
313     {
314         IgnoreError(SendDataPoll());
315     }
316     else
317     {
318         mPollTimeoutCounter = 0;
319     }
320 
321 exit:
322     return;
323 }
324 
ProcessRxFrame(const Mac::RxFrame & aFrame)325 void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame)
326 {
327     VerifyOrExit(mEnabled);
328 
329     mPollTimeoutCounter = 0;
330 
331     if (aFrame.GetFramePending())
332     {
333         IgnoreError(SendDataPoll());
334     }
335 
336 exit:
337     return;
338 }
339 
340 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
ProcessTxDone(const Mac::TxFrame & aFrame,const Mac::RxFrame * aAckFrame,Error aError)341 void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError)
342 {
343     bool sendDataPoll = false;
344 
345     VerifyOrExit(mEnabled);
346     VerifyOrExit(Get<Mle::MleRouter>().GetParent().IsEnhancedKeepAliveSupported());
347     VerifyOrExit(aFrame.GetSecurityEnabled());
348 
349 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
350     if (aFrame.mInfo.mTxInfo.mIsARetx && (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr))
351     {
352         // For retransmission frame, use a data poll to resync its parent with correct CSL phase
353         sendDataPoll = true;
354     }
355 #endif
356 
357     if (aError == kErrorNone && aAckFrame != nullptr)
358     {
359         mPollTimeoutCounter = 0;
360 
361         if (aAckFrame->GetFramePending())
362         {
363             sendDataPoll = true;
364         }
365         else
366         {
367             ResetKeepAliveTimer();
368         }
369     }
370 
371     if (sendDataPoll)
372     {
373         IgnoreError(SendDataPoll());
374     }
375 
376 exit:
377     return;
378 }
379 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
380 
RecalculatePollPeriod(void)381 void DataPollSender::RecalculatePollPeriod(void)
382 {
383     if (mEnabled)
384     {
385         ScheduleNextPoll(kRecalculatePollPeriod);
386     }
387 }
388 
SetAttachMode(bool aMode)389 void DataPollSender::SetAttachMode(bool aMode)
390 {
391     if (mAttachMode != aMode)
392     {
393         mAttachMode = aMode;
394 
395         if (mEnabled)
396         {
397             ScheduleNextPoll(kRecalculatePollPeriod);
398         }
399     }
400 }
401 
SendFastPolls(uint8_t aNumFastPolls)402 void DataPollSender::SendFastPolls(uint8_t aNumFastPolls)
403 {
404     bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0);
405 
406     if (mFastPollsUsers < kMaxFastPollsUsers)
407     {
408         mFastPollsUsers++;
409     }
410 
411     if (aNumFastPolls == 0)
412     {
413         aNumFastPolls = kDefaultFastPolls;
414     }
415 
416     if (aNumFastPolls > kMaxFastPolls)
417     {
418         aNumFastPolls = kMaxFastPolls;
419     }
420 
421     if (mRemainingFastPolls < aNumFastPolls)
422     {
423         mRemainingFastPolls = aNumFastPolls;
424     }
425 
426     if (mEnabled && shouldRecalculatePollPeriod)
427     {
428         ScheduleNextPoll(kRecalculatePollPeriod);
429     }
430 }
431 
StopFastPolls(void)432 void DataPollSender::StopFastPolls(void)
433 {
434     VerifyOrExit(mFastPollsUsers != 0);
435 
436     // If `mFastPollsUsers` hits the max, let it be cleared
437     // from `HandlePollSent()` (after all fast polls are sent).
438     VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
439 
440     mFastPollsUsers--;
441 
442     VerifyOrExit(mFastPollsUsers == 0);
443 
444     mRemainingFastPolls = 0;
445     ScheduleNextPoll(kRecalculatePollPeriod);
446 
447 exit:
448     return;
449 }
450 
ResetKeepAliveTimer(void)451 void DataPollSender::ResetKeepAliveTimer(void)
452 {
453     if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
454     {
455         mTimerStartTime = TimerMilli::GetNow();
456         mTimer.StartAt(mTimerStartTime, mPollPeriod);
457     }
458 }
459 
ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)460 void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
461 {
462     TimeMilli now;
463     uint32_t  oldPeriod = mPollPeriod;
464 
465     if (aPollPeriodSelector == kRecalculatePollPeriod)
466     {
467         mPollPeriod = CalculatePollPeriod();
468     }
469 
470     now = TimerMilli::GetNow();
471 
472     if (mTimer.IsRunning())
473     {
474         if (oldPeriod != mPollPeriod)
475         {
476             // If poll interval did change and re-starting the timer from
477             // last start time with new poll interval would fire quickly
478             // (i.e., fires within window `[now, now + kMinPollPeriod]`)
479             // add an extra minimum delay of `kMinPollPeriod`. This
480             // ensures that when an internal or external request triggers
481             // a switch to a shorter poll interval, the first data poll
482             // will not be sent too quickly (and possibly before the
483             // response is available/prepared on the parent node).
484 
485             if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
486             {
487                 mTimer.StartAt(now, kMinPollPeriod);
488             }
489             else
490             {
491                 mTimer.StartAt(mTimerStartTime, mPollPeriod);
492             }
493         }
494         // Do nothing on the running poll timer if the poll interval doesn't change
495     }
496     else
497     {
498         mTimerStartTime = now;
499         mTimer.StartAt(mTimerStartTime, mPollPeriod);
500     }
501 }
502 
CalculatePollPeriod(void) const503 uint32_t DataPollSender::CalculatePollPeriod(void) const
504 {
505     uint32_t period = GetDefaultPollPeriod();
506 
507     if (mAttachMode)
508     {
509         period = OT_MIN(period, kAttachDataPollPeriod);
510     }
511 
512     if (mRetxMode)
513     {
514         period = OT_MIN(period, kRetxPollPeriod);
515     }
516 
517     if (mRemainingFastPolls != 0)
518     {
519         period = OT_MIN(period, kFastPollPeriod);
520     }
521 
522     if (mExternalPollPeriod != 0)
523     {
524         period = OT_MIN(period, mExternalPollPeriod);
525     }
526 
527     if (period == 0)
528     {
529         period = kMinPollPeriod;
530     }
531 
532     return period;
533 }
534 
HandlePollTimer(Timer & aTimer)535 void DataPollSender::HandlePollTimer(Timer &aTimer)
536 {
537     IgnoreError(aTimer.Get<DataPollSender>().SendDataPoll());
538 }
539 
GetDefaultPollPeriod(void) const540 uint32_t DataPollSender::GetDefaultPollPeriod(void) const
541 {
542     uint32_t period    = Time::SecToMsec(Get<Mle::MleRouter>().GetTimeout());
543     uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
544 
545 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
546     if (Get<Mac::Mac>().IsCslEnabled())
547     {
548         period    = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
549         pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
550     }
551 #endif
552 
553     if (period > pollAhead)
554     {
555         period -= pollAhead;
556     }
557 
558     return period;
559 }
560 
PrepareDataRequest(Mac::TxFrames & aTxFrames)561 Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
562 {
563     Mac::TxFrame *frame = nullptr;
564     Mac::Address  src, dst;
565     uint16_t      fcf;
566     bool          iePresent;
567 
568 #if OPENTHREAD_CONFIG_MULTI_RADIO
569     Mac::RadioType radio;
570 
571     SuccessOrExit(GetPollDestinationAddress(dst, radio));
572     frame = &aTxFrames.GetTxFrame(radio);
573 #else
574     SuccessOrExit(GetPollDestinationAddress(dst));
575     frame = &aTxFrames.GetTxFrame();
576 #endif
577 
578     fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfAckRequest |
579           Mac::Frame::kFcfSecurityEnabled;
580 
581     iePresent = Get<MeshForwarder>().CalcIePresent(nullptr);
582 
583     if (iePresent)
584     {
585         fcf |= Mac::Frame::kFcfIePresent;
586     }
587 
588     fcf |= Get<MeshForwarder>().CalcFrameVersion(Get<NeighborTable>().FindNeighbor(dst), iePresent);
589 
590     if (dst.IsExtended())
591     {
592         fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt;
593         src.SetExtended(Get<Mac::Mac>().GetExtAddress());
594     }
595     else
596     {
597         fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort;
598         src.SetShort(Get<Mac::Mac>().GetShortAddress());
599     }
600 
601     frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
602 
603     if (frame->IsDstPanIdPresent())
604     {
605         frame->SetDstPanId(Get<Mac::Mac>().GetPanId());
606     }
607 
608     frame->SetSrcAddr(src);
609     frame->SetDstAddr(dst);
610 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
611     if (iePresent)
612     {
613         Get<MeshForwarder>().AppendHeaderIe(nullptr, *frame);
614     }
615 
616 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
617     if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)
618     {
619         // Disable frame retransmission when the data poll has CSL IE included
620         aTxFrames.SetMaxFrameRetries(0);
621     }
622 #endif
623 #endif
624 
625     IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest));
626 
627 exit:
628     return frame;
629 }
630 
631 } // namespace ot
632