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