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