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 definitions for handling indirect transmission.
32  */
33 
34 #include "indirect_sender.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/message.hpp"
42 #include "thread/child.hpp"
43 #include "thread/mesh_forwarder.hpp"
44 #include "thread/mle_tlvs.hpp"
45 
46 namespace ot {
47 
GetMacAddress(Mac::Address & aMacAddress) const48 const Mac::Address &IndirectSender::ChildInfo::GetMacAddress(Mac::Address &aMacAddress) const
49 {
50     if (mUseShortAddress)
51     {
52         aMacAddress.SetShort(static_cast<const Child *>(this)->GetRloc16());
53     }
54     else
55     {
56         aMacAddress.SetExtended(static_cast<const Child *>(this)->GetExtAddress());
57     }
58 
59     return aMacAddress;
60 }
61 
IndirectSender(Instance & aInstance)62 IndirectSender::IndirectSender(Instance &aInstance)
63     : InstanceLocator(aInstance)
64     , mEnabled(false)
65     , mSourceMatchController(aInstance)
66     , mDataPollHandler(aInstance)
67 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
68     , mCslTxScheduler(aInstance)
69 #endif
70 {
71 }
72 
Stop(void)73 void IndirectSender::Stop(void)
74 {
75     VerifyOrExit(mEnabled);
76 
77     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
78     {
79         child.SetIndirectMessage(nullptr);
80         mSourceMatchController.ResetMessageCount(child);
81     }
82 
83     mDataPollHandler.Clear();
84 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
85     mCslTxScheduler.Clear();
86 #endif
87 
88 exit:
89     mEnabled = false;
90 }
91 
AddMessageForSleepyChild(Message & aMessage,Child & aChild)92 void IndirectSender::AddMessageForSleepyChild(Message &aMessage, Child &aChild)
93 {
94     uint16_t childIndex;
95 
96     OT_ASSERT(!aChild.IsRxOnWhenIdle());
97 
98     childIndex = Get<ChildTable>().GetChildIndex(aChild);
99     VerifyOrExit(!aMessage.GetChildMask(childIndex));
100 
101     aMessage.SetChildMask(childIndex);
102     mSourceMatchController.IncrementMessageCount(aChild);
103 
104     if ((aMessage.GetType() != Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1))
105     {
106         Message *supervisionMessage = FindIndirectMessage(aChild, /* aSupervisionTypeOnly */ true);
107 
108         if (supervisionMessage != nullptr)
109         {
110             IgnoreError(RemoveMessageFromSleepyChild(*supervisionMessage, aChild));
111             Get<MeshForwarder>().RemoveMessageIfNoPendingTx(*supervisionMessage);
112         }
113     }
114 
115     RequestMessageUpdate(aChild);
116 
117 exit:
118     return;
119 }
120 
RemoveMessageFromSleepyChild(Message & aMessage,Child & aChild)121 Error IndirectSender::RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild)
122 {
123     Error    error      = kErrorNone;
124     uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
125 
126     VerifyOrExit(aMessage.GetChildMask(childIndex), error = kErrorNotFound);
127 
128     aMessage.ClearChildMask(childIndex);
129     mSourceMatchController.DecrementMessageCount(aChild);
130 
131     RequestMessageUpdate(aChild);
132 
133 exit:
134     return error;
135 }
136 
ClearAllMessagesForSleepyChild(Child & aChild)137 void IndirectSender::ClearAllMessagesForSleepyChild(Child &aChild)
138 {
139     VerifyOrExit(aChild.GetIndirectMessageCount() > 0);
140 
141     for (Message &message : Get<MeshForwarder>().mSendQueue)
142     {
143         message.ClearChildMask(Get<ChildTable>().GetChildIndex(aChild));
144 
145         Get<MeshForwarder>().RemoveMessageIfNoPendingTx(message);
146     }
147 
148     aChild.SetIndirectMessage(nullptr);
149     mSourceMatchController.ResetMessageCount(aChild);
150 
151     mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
152 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
153     mCslTxScheduler.Update();
154 #endif
155 
156 exit:
157     return;
158 }
159 
SetChildUseShortAddress(Child & aChild,bool aUseShortAddress)160 void IndirectSender::SetChildUseShortAddress(Child &aChild, bool aUseShortAddress)
161 {
162     VerifyOrExit(aChild.IsIndirectSourceMatchShort() != aUseShortAddress);
163 
164     mSourceMatchController.SetSrcMatchAsShort(aChild, aUseShortAddress);
165 
166 exit:
167     return;
168 }
169 
HandleChildModeChange(Child & aChild,Mle::DeviceMode aOldMode)170 void IndirectSender::HandleChildModeChange(Child &aChild, Mle::DeviceMode aOldMode)
171 {
172     if (!aChild.IsRxOnWhenIdle() && (aChild.IsStateValid()))
173     {
174         SetChildUseShortAddress(aChild, true);
175     }
176 
177     // On sleepy to non-sleepy mode change, convert indirect messages in
178     // the send queue destined to the child to direct.
179 
180     if (!aOldMode.IsRxOnWhenIdle() && aChild.IsRxOnWhenIdle() && (aChild.GetIndirectMessageCount() > 0))
181     {
182         uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
183 
184         for (Message &message : Get<MeshForwarder>().mSendQueue)
185         {
186             if (message.GetChildMask(childIndex))
187             {
188                 message.ClearChildMask(childIndex);
189                 message.SetDirectTransmission();
190             }
191         }
192 
193         aChild.SetIndirectMessage(nullptr);
194         mSourceMatchController.ResetMessageCount(aChild);
195 
196         mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
197 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
198         mCslTxScheduler.Update();
199 #endif
200     }
201 
202     // Since the queuing delays for direct transmissions are expected to
203     // be relatively small especially when compared to indirect, for a
204     // non-sleepy to sleepy mode change, we allow any direct message
205     // (for the child) already in the send queue to remain as is. This
206     // is equivalent to dropping the already queued messages in this
207     // case.
208 }
209 
FindIndirectMessage(Child & aChild,bool aSupervisionTypeOnly)210 Message *IndirectSender::FindIndirectMessage(Child &aChild, bool aSupervisionTypeOnly)
211 {
212     Message *msg        = nullptr;
213     uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
214 
215     for (Message &message : Get<MeshForwarder>().mSendQueue)
216     {
217         if (message.GetChildMask(childIndex) &&
218             (!aSupervisionTypeOnly || (message.GetType() == Message::kTypeSupervision)))
219         {
220             msg = &message;
221             break;
222         }
223     }
224 
225     return msg;
226 }
227 
RequestMessageUpdate(Child & aChild)228 void IndirectSender::RequestMessageUpdate(Child &aChild)
229 {
230     Message *curMessage = aChild.GetIndirectMessage();
231     Message *newMessage;
232 
233     // Purge the frame if the current message is no longer destined
234     // for the child. This check needs to be done first to cover the
235     // case where we have a pending "replace frame" request and while
236     // waiting for the callback, the current message is removed.
237 
238     if ((curMessage != nullptr) && !curMessage->GetChildMask(Get<ChildTable>().GetChildIndex(aChild)))
239     {
240         // Set the indirect message for this child to `nullptr` to ensure
241         // it is not processed on `HandleSentFrameToChild()` callback.
242 
243         aChild.SetIndirectMessage(nullptr);
244 
245         // Request a "frame purge" using `RequestFrameChange()` and
246         // wait for `HandleFrameChangeDone()` callback for completion
247         // of the request. Note that the callback may be directly
248         // called from the `RequestFrameChange()` itself when the
249         // request can be handled immediately.
250 
251         aChild.SetWaitingForMessageUpdate(true);
252         mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
253 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
254         mCslTxScheduler.Update();
255 #endif
256 
257         ExitNow();
258     }
259 
260     VerifyOrExit(!aChild.IsWaitingForMessageUpdate());
261 
262     newMessage = FindIndirectMessage(aChild);
263 
264     VerifyOrExit(curMessage != newMessage);
265 
266     if (curMessage == nullptr)
267     {
268         // Current message is `nullptr`, but new message is not.
269         // We have a new indirect message.
270 
271         UpdateIndirectMessage(aChild);
272         ExitNow();
273     }
274 
275     // Current message and new message differ and are both
276     // non-`nullptr`. We need to request the frame to be replaced.
277     // The current indirect message can be replaced only if it is
278     // the first fragment. If a next fragment frame for message is
279     // already prepared, we wait for the entire message to be
280     // delivered.
281 
282     VerifyOrExit(aChild.GetIndirectFragmentOffset() == 0);
283 
284     aChild.SetWaitingForMessageUpdate(true);
285     mDataPollHandler.RequestFrameChange(DataPollHandler::kReplaceFrame, aChild);
286 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
287     mCslTxScheduler.Update();
288 #endif
289 
290 exit:
291     return;
292 }
293 
HandleFrameChangeDone(Child & aChild)294 void IndirectSender::HandleFrameChangeDone(Child &aChild)
295 {
296     VerifyOrExit(aChild.IsWaitingForMessageUpdate());
297     UpdateIndirectMessage(aChild);
298 
299 exit:
300     return;
301 }
302 
UpdateIndirectMessage(Child & aChild)303 void IndirectSender::UpdateIndirectMessage(Child &aChild)
304 {
305     Message *message = FindIndirectMessage(aChild);
306 
307     aChild.SetWaitingForMessageUpdate(false);
308     aChild.SetIndirectMessage(message);
309     aChild.SetIndirectFragmentOffset(0);
310     aChild.SetIndirectTxSuccess(true);
311 
312 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
313     mCslTxScheduler.Update();
314 #endif
315 
316     if (message != nullptr)
317     {
318         Mac::Address childAddress;
319 
320         mDataPollHandler.HandleNewFrame(aChild);
321 
322         aChild.GetMacAddress(childAddress);
323         Get<MeshForwarder>().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, kErrorNone, &childAddress);
324     }
325 }
326 
PrepareFrameForChild(Mac::TxFrame & aFrame,FrameContext & aContext,Child & aChild)327 Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild)
328 {
329     Error    error   = kErrorNone;
330     Message *message = aChild.GetIndirectMessage();
331 
332     VerifyOrExit(mEnabled, error = kErrorAbort);
333 
334     if (message == nullptr)
335     {
336         PrepareEmptyFrame(aFrame, aChild, /* aAckRequest */ true);
337         aContext.mMessageNextOffset = 0;
338         ExitNow();
339     }
340 
341     switch (message->GetType())
342     {
343     case Message::kTypeIp6:
344         aContext.mMessageNextOffset = PrepareDataFrame(aFrame, aChild, *message);
345         break;
346 
347     case Message::kTypeSupervision:
348         PrepareEmptyFrame(aFrame, aChild, /* aAckRequest */ true);
349         aContext.mMessageNextOffset = message->GetLength();
350         break;
351 
352     default:
353         OT_ASSERT(false);
354     }
355 
356 exit:
357     return error;
358 }
359 
PrepareDataFrame(Mac::TxFrame & aFrame,Child & aChild,Message & aMessage)360 uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, Message &aMessage)
361 {
362     Ip6::Header    ip6Header;
363     Mac::Addresses macAddrs;
364     uint16_t       directTxOffset;
365     uint16_t       nextOffset;
366 
367     // Determine the MAC source and destination addresses.
368 
369     IgnoreError(aMessage.Read(0, ip6Header));
370 
371     Get<MeshForwarder>().GetMacSourceAddress(ip6Header.GetSource(), macAddrs.mSource);
372 
373     if (ip6Header.GetDestination().IsLinkLocal())
374     {
375         Get<MeshForwarder>().GetMacDestinationAddress(ip6Header.GetDestination(), macAddrs.mDestination);
376     }
377     else
378     {
379         aChild.GetMacAddress(macAddrs.mDestination);
380     }
381 
382     // Prepare the data frame from previous child's indirect offset.
383 
384     directTxOffset = aMessage.GetOffset();
385     aMessage.SetOffset(aChild.GetIndirectFragmentOffset());
386 
387     nextOffset = Get<MeshForwarder>().PrepareDataFrameWithNoMeshHeader(aFrame, aMessage, macAddrs);
388 
389     aMessage.SetOffset(directTxOffset);
390 
391     // Set `FramePending` if there are more queued messages (excluding
392     // the current one being sent out) for the child (note `> 1` check).
393     // The case where the current message itself requires fragmentation
394     // is already checked and handled in `PrepareDataFrame()` method.
395 
396     if (aChild.GetIndirectMessageCount() > 1)
397     {
398         aFrame.SetFramePending(true);
399     }
400 
401     return nextOffset;
402 }
403 
PrepareEmptyFrame(Mac::TxFrame & aFrame,Child & aChild,bool aAckRequest)404 void IndirectSender::PrepareEmptyFrame(Mac::TxFrame &aFrame, Child &aChild, bool aAckRequest)
405 {
406     Mac::Address macDest;
407     aChild.GetMacAddress(macDest);
408     Get<MeshForwarder>().PrepareEmptyFrame(aFrame, macDest, aAckRequest);
409 }
410 
HandleSentFrameToChild(const Mac::TxFrame & aFrame,const FrameContext & aContext,Error aError,Child & aChild)411 void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
412                                             const FrameContext &aContext,
413                                             Error               aError,
414                                             Child              &aChild)
415 {
416     Message *message    = aChild.GetIndirectMessage();
417     uint16_t nextOffset = aContext.mMessageNextOffset;
418 
419     VerifyOrExit(mEnabled);
420 
421     if (aError == kErrorNone)
422     {
423         Get<ChildSupervisor>().UpdateOnSend(aChild);
424     }
425 
426     // A zero `nextOffset` indicates that the sent frame is an empty
427     // frame generated by `PrepareFrameForChild()` when there was no
428     // indirect message in the send queue for the child. This can happen
429     // in the (not common) case where the radio platform does not
430     // support the "source address match" feature and always includes
431     // "frame pending" flag in acks to data poll frames. In such a case,
432     // `IndirectSender` prepares and sends an empty frame to the child
433     // after it sends a data poll. Here in `HandleSentFrameToChild()` we
434     // exit quickly if we detect the "send done" is for the empty frame
435     // to ensure we do not update any newly added indirect message after
436     // preparing the empty frame.
437 
438     VerifyOrExit(nextOffset != 0);
439 
440     switch (aError)
441     {
442     case kErrorNone:
443         break;
444 
445     case kErrorNoAck:
446     case kErrorChannelAccessFailure:
447     case kErrorAbort:
448 
449         aChild.SetIndirectTxSuccess(false);
450 
451 #if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
452         // We set the nextOffset to end of message, since there is no need to
453         // send any remaining fragments in the message to the child, if all tx
454         // attempts of current frame already failed.
455 
456         if (message != nullptr)
457         {
458             nextOffset = message->GetLength();
459         }
460 #endif
461         break;
462 
463     default:
464         OT_ASSERT(false);
465     }
466 
467     if ((message != nullptr) && (nextOffset < message->GetLength()))
468     {
469         aChild.SetIndirectFragmentOffset(nextOffset);
470         mDataPollHandler.HandleNewFrame(aChild);
471 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
472         mCslTxScheduler.Update();
473 #endif
474         ExitNow();
475     }
476 
477     if (message != nullptr)
478     {
479         // The indirect tx of this message to the child is done.
480 
481         Error        txError    = aError;
482         uint16_t     childIndex = Get<ChildTable>().GetChildIndex(aChild);
483         Mac::Address macDest;
484 
485         aChild.SetIndirectMessage(nullptr);
486         aChild.GetLinkInfo().AddMessageTxStatus(aChild.GetIndirectTxSuccess());
487 
488         // Enable short source address matching after the first indirect
489         // message transmission attempt to the child. We intentionally do
490         // not check for successful tx here to address the scenario where
491         // the child does receive "Child ID Response" but parent misses the
492         // 15.4 ack from child. If the "Child ID Response" does not make it
493         // to the child, then the child will need to send a new "Child ID
494         // Request" which will cause the parent to switch to using long
495         // address mode for source address matching.
496 
497         mSourceMatchController.SetSrcMatchAsShort(aChild, true);
498 
499 #if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
500 
501         // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
502         // disabled, all fragment frames of a larger message are
503         // sent even if the transmission of an earlier fragment fail.
504         // Note that `GetIndirectTxSuccess() tracks the tx success of
505         // the entire message to the child, while `txError = aError`
506         // represents the error status of the last fragment frame
507         // transmission.
508 
509         if (!aChild.GetIndirectTxSuccess() && (txError == kErrorNone))
510         {
511             txError = kErrorFailed;
512         }
513 #endif
514 
515         if (!aFrame.IsEmpty())
516         {
517             IgnoreError(aFrame.GetDstAddr(macDest));
518             Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *message, txError, &macDest);
519         }
520 
521         if (message->GetType() == Message::kTypeIp6)
522         {
523             if (aChild.GetIndirectTxSuccess())
524             {
525                 Get<MeshForwarder>().mIpCounters.mTxSuccess++;
526             }
527             else
528             {
529                 Get<MeshForwarder>().mIpCounters.mTxFailure++;
530             }
531         }
532 
533         if (message->GetChildMask(childIndex))
534         {
535             message->ClearChildMask(childIndex);
536             mSourceMatchController.DecrementMessageCount(aChild);
537         }
538 
539         Get<MeshForwarder>().RemoveMessageIfNoPendingTx(*message);
540     }
541 
542     UpdateIndirectMessage(aChild);
543 
544 exit:
545     if (mEnabled)
546     {
547         ClearMessagesForRemovedChildren();
548     }
549 }
550 
ClearMessagesForRemovedChildren(void)551 void IndirectSender::ClearMessagesForRemovedChildren(void)
552 {
553     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptValidOrRestoring))
554     {
555         if (child.GetIndirectMessageCount() == 0)
556         {
557             continue;
558         }
559 
560         ClearAllMessagesForSleepyChild(child);
561     }
562 }
563 
564 } // namespace ot
565 
566 #endif // #if OPENTHREAD_FTD
567