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