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