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