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