1 /*
2 * Copyright (c) 2019, 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 includes the implementation for handling of data polls and indirect frame transmission.
32 */
33
34 #include "data_poll_handler.hpp"
35
36 #if OPENTHREAD_FTD
37
38 #include "common/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42
43 namespace ot {
44
Callbacks(Instance & aInstance)45 DataPollHandler::Callbacks::Callbacks(Instance &aInstance)
46 : InstanceLocator(aInstance)
47 {
48 }
49
PrepareFrameForChild(Mac::TxFrame & aFrame,FrameContext & aContext,Child & aChild)50 inline Error DataPollHandler::Callbacks::PrepareFrameForChild(Mac::TxFrame &aFrame,
51 FrameContext &aContext,
52 Child & aChild)
53 {
54 return Get<IndirectSender>().PrepareFrameForChild(aFrame, aContext, aChild);
55 }
56
HandleSentFrameToChild(const Mac::TxFrame & aFrame,const FrameContext & aContext,Error aError,Child & aChild)57 inline void DataPollHandler::Callbacks::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
58 const FrameContext &aContext,
59 Error aError,
60 Child & aChild)
61 {
62 Get<IndirectSender>().HandleSentFrameToChild(aFrame, aContext, aError, aChild);
63 }
64
HandleFrameChangeDone(Child & aChild)65 inline void DataPollHandler::Callbacks::HandleFrameChangeDone(Child &aChild)
66 {
67 Get<IndirectSender>().HandleFrameChangeDone(aChild);
68 }
69
70 //---------------------------------------------------------
71
DataPollHandler(Instance & aInstance)72 DataPollHandler::DataPollHandler(Instance &aInstance)
73 : InstanceLocator(aInstance)
74 , mIndirectTxChild(nullptr)
75 , mFrameContext()
76 , mCallbacks(aInstance)
77 {
78 }
79
Clear(void)80 void DataPollHandler::Clear(void)
81 {
82 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
83 {
84 child.SetDataPollPending(false);
85 child.SetFrameReplacePending(false);
86 child.SetFramePurgePending(false);
87 child.ResetIndirectTxAttempts();
88 }
89
90 mIndirectTxChild = nullptr;
91 }
92
HandleNewFrame(Child & aChild)93 void DataPollHandler::HandleNewFrame(Child &aChild)
94 {
95 OT_UNUSED_VARIABLE(aChild);
96
97 // There is no need to take any action with current data poll
98 // handler implementation, since the preparation of the frame
99 // happens after receiving of a data poll from the child. This
100 // method is included for use by other data poll handler models
101 // (e.g., in RCP/host model if the handling of data polls is
102 // delegated to RCP).
103 }
104
RequestFrameChange(FrameChange aChange,Child & aChild)105 void DataPollHandler::RequestFrameChange(FrameChange aChange, Child &aChild)
106 {
107 if ((mIndirectTxChild == &aChild) && Get<Mac::Mac>().IsPerformingIndirectTransmit())
108 {
109 switch (aChange)
110 {
111 case kReplaceFrame:
112 aChild.SetFrameReplacePending(true);
113 break;
114
115 case kPurgeFrame:
116 aChild.SetFramePurgePending(true);
117 break;
118 }
119 }
120 else
121 {
122 mCallbacks.HandleFrameChangeDone(aChild);
123 }
124 }
125
HandleDataPoll(Mac::RxFrame & aFrame)126 void DataPollHandler::HandleDataPoll(Mac::RxFrame &aFrame)
127 {
128 Mac::Address macSource;
129 Child * child;
130 uint16_t indirectMsgCount;
131
132 VerifyOrExit(aFrame.GetSecurityEnabled());
133 VerifyOrExit(!Get<Mle::MleRouter>().IsDetached());
134
135 SuccessOrExit(aFrame.GetSrcAddr(macSource));
136 child = Get<ChildTable>().FindChild(macSource, Child::kInStateValidOrRestoring);
137 VerifyOrExit(child != nullptr);
138
139 child->SetLastHeard(TimerMilli::GetNow());
140 child->ResetLinkFailures();
141 #if OPENTHREAD_CONFIG_MULTI_RADIO
142 child->SetLastPollRadioType(aFrame.GetRadioType());
143 #endif
144
145 indirectMsgCount = child->GetIndirectMessageCount();
146
147 otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
148 aFrame.GetRssi(), aFrame.IsAckedWithFramePending());
149
150 if (!aFrame.IsAckedWithFramePending())
151 {
152 if ((indirectMsgCount > 0) && macSource.IsShort())
153 {
154 Get<SourceMatchController>().SetSrcMatchAsShort(*child, true);
155 }
156
157 ExitNow();
158 }
159
160 if (mIndirectTxChild == nullptr)
161 {
162 mIndirectTxChild = child;
163 Get<Mac::Mac>().RequestIndirectFrameTransmission();
164 }
165 else
166 {
167 child->SetDataPollPending(true);
168 }
169
170 exit:
171 return;
172 }
173
HandleFrameRequest(Mac::TxFrames & aTxFrames)174 Mac::TxFrame *DataPollHandler::HandleFrameRequest(Mac::TxFrames &aTxFrames)
175 {
176 Mac::TxFrame *frame = nullptr;
177
178 VerifyOrExit(mIndirectTxChild != nullptr);
179
180 #if OPENTHREAD_CONFIG_MULTI_RADIO
181 frame = &aTxFrames.GetTxFrame(mIndirectTxChild->GetLastPollRadioType());
182 #else
183 frame = &aTxFrames.GetTxFrame();
184 #endif
185
186 VerifyOrExit(mCallbacks.PrepareFrameForChild(*frame, mFrameContext, *mIndirectTxChild) == kErrorNone,
187 frame = nullptr);
188
189 if (mIndirectTxChild->GetIndirectTxAttempts() > 0)
190 {
191 // For a re-transmission of an indirect frame to a sleepy
192 // child, we ensure to use the same frame counter, key id, and
193 // data sequence number as the previous attempt.
194
195 frame->SetIsARetransmission(true);
196 frame->SetSequence(mIndirectTxChild->GetIndirectDataSequenceNumber());
197
198 if (frame->GetSecurityEnabled())
199 {
200 frame->SetFrameCounter(mIndirectTxChild->GetIndirectFrameCounter());
201 frame->SetKeyId(mIndirectTxChild->GetIndirectKeyId());
202 }
203 }
204 else
205 {
206 frame->SetIsARetransmission(false);
207 }
208
209 exit:
210 return frame;
211 }
212
HandleSentFrame(const Mac::TxFrame & aFrame,Error aError)213 void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError)
214 {
215 Child *child = mIndirectTxChild;
216
217 VerifyOrExit(child != nullptr);
218
219 mIndirectTxChild = nullptr;
220 HandleSentFrame(aFrame, aError, *child);
221
222 exit:
223 ProcessPendingPolls();
224 }
225
HandleSentFrame(const Mac::TxFrame & aFrame,Error aError,Child & aChild)226 void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, Child &aChild)
227 {
228 if (aChild.IsFramePurgePending())
229 {
230 aChild.SetFramePurgePending(false);
231 aChild.SetFrameReplacePending(false);
232 aChild.ResetIndirectTxAttempts();
233 mCallbacks.HandleFrameChangeDone(aChild);
234 ExitNow();
235 }
236
237 switch (aError)
238 {
239 case kErrorNone:
240 aChild.ResetIndirectTxAttempts();
241 aChild.SetFrameReplacePending(false);
242 break;
243
244 case kErrorNoAck:
245 OT_ASSERT(!aFrame.GetSecurityEnabled() || aFrame.IsHeaderUpdated());
246
247 aChild.IncrementIndirectTxAttempts();
248 otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(),
249 aChild.GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts);
250
251 OT_FALL_THROUGH;
252
253 case kErrorChannelAccessFailure:
254 case kErrorAbort:
255
256 if (aChild.IsFrameReplacePending())
257 {
258 aChild.SetFrameReplacePending(false);
259 aChild.ResetIndirectTxAttempts();
260 mCallbacks.HandleFrameChangeDone(aChild);
261 ExitNow();
262 }
263
264 if ((aChild.GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts) && !aFrame.IsEmpty())
265 {
266 // We save the frame counter, key id, and data sequence number of
267 // current frame so we use the same values for the retransmission
268 // of the frame following the receipt of the next data poll.
269
270 aChild.SetIndirectDataSequenceNumber(aFrame.GetSequence());
271
272 if (aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated())
273 {
274 uint32_t frameCounter;
275 uint8_t keyId;
276 Error error;
277
278 error = aFrame.GetFrameCounter(frameCounter);
279 OT_ASSERT(error == kErrorNone);
280 aChild.SetIndirectFrameCounter(frameCounter);
281
282 error = aFrame.GetKeyId(keyId);
283 OT_ASSERT(error == kErrorNone);
284 aChild.SetIndirectKeyId(keyId);
285
286 OT_UNUSED_VARIABLE(error);
287 }
288
289 ExitNow();
290 }
291
292 aChild.ResetIndirectTxAttempts();
293 break;
294
295 default:
296 OT_ASSERT(false);
297 OT_UNREACHABLE_CODE(break);
298 }
299
300 mCallbacks.HandleSentFrameToChild(aFrame, mFrameContext, aError, aChild);
301
302 exit:
303 return;
304 }
305
ProcessPendingPolls(void)306 void DataPollHandler::ProcessPendingPolls(void)
307 {
308 for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
309 {
310 if (!child.IsDataPollPending())
311 {
312 continue;
313 }
314
315 // Find the child with earliest poll receive time.
316
317 if ((mIndirectTxChild == nullptr) || (child.GetLastHeard() < mIndirectTxChild->GetLastHeard()))
318 {
319 mIndirectTxChild = &child;
320 }
321 }
322
323 if (mIndirectTxChild != nullptr)
324 {
325 mIndirectTxChild->SetDataPollPending(false);
326 Get<Mac::Mac>().RequestIndirectFrameTransmission();
327 }
328 }
329
330 } // namespace ot
331
332 #endif // #if OPENTHREAD_FTD
333