• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/mesh_forwarder.hpp"
43 #include "thread/mle_tlvs.hpp"
44 #include "thread/topology.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, kSupervisionMsgAckRequest);
349         aContext.mMessageNextOffset = message->GetLength();
350         break;
351 
352     default:
353         OT_ASSERT(false);
354         OT_UNREACHABLE_CODE(break);
355     }
356 
357 exit:
358     return error;
359 }
360 
PrepareDataFrame(Mac::TxFrame & aFrame,Child & aChild,Message & aMessage)361 uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, Message &aMessage)
362 {
363     Ip6::Header  ip6Header;
364     Mac::Address macSource, macDest;
365     uint16_t     directTxOffset;
366     uint16_t     nextOffset;
367 
368     // Determine the MAC source and destination addresses.
369 
370     IgnoreError(aMessage.Read(0, ip6Header));
371 
372     Get<MeshForwarder>().GetMacSourceAddress(ip6Header.GetSource(), macSource);
373 
374     if (ip6Header.GetDestination().IsLinkLocal())
375     {
376         Get<MeshForwarder>().GetMacDestinationAddress(ip6Header.GetDestination(), macDest);
377     }
378     else
379     {
380         aChild.GetMacAddress(macDest);
381     }
382 
383     // Prepare the data frame from previous child's indirect offset.
384 
385     directTxOffset = aMessage.GetOffset();
386     aMessage.SetOffset(aChild.GetIndirectFragmentOffset());
387 
388     nextOffset = Get<MeshForwarder>().PrepareDataFrame(aFrame, aMessage, macSource, macDest);
389 
390     aMessage.SetOffset(directTxOffset);
391 
392     // Set `FramePending` if there are more queued messages (excluding
393     // the current one being sent out) for the child (note `> 1` check).
394     // The case where the current message itself requires fragmentation
395     // is already checked and handled in `PrepareDataFrame()` method.
396 
397     if (aChild.GetIndirectMessageCount() > 1)
398     {
399         aFrame.SetFramePending(true);
400     }
401 
402     return nextOffset;
403 }
404 
PrepareEmptyFrame(Mac::TxFrame & aFrame,Child & aChild,bool aAckRequest)405 void IndirectSender::PrepareEmptyFrame(Mac::TxFrame &aFrame, Child &aChild, bool aAckRequest)
406 {
407     Mac::Address macDest;
408     aChild.GetMacAddress(macDest);
409     Get<MeshForwarder>().PrepareEmptyFrame(aFrame, macDest, aAckRequest);
410 }
411 
HandleSentFrameToChild(const Mac::TxFrame & aFrame,const FrameContext & aContext,Error aError,Child & aChild)412 void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame,
413                                             const FrameContext &aContext,
414                                             Error               aError,
415                                             Child &             aChild)
416 {
417     Message *message    = aChild.GetIndirectMessage();
418     uint16_t nextOffset = aContext.mMessageNextOffset;
419 
420     VerifyOrExit(mEnabled);
421 
422 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
423     if (aError == kErrorNone)
424     {
425         Get<Utils::ChildSupervisor>().UpdateOnSend(aChild);
426     }
427 #endif
428 
429     // A zero `nextOffset` indicates that the sent frame is an empty
430     // frame generated by `PrepareFrameForChild()` when there was no
431     // indirect message in the send queue for the child. This can happen
432     // in the (not common) case where the radio platform does not
433     // support the "source address match" feature and always includes
434     // "frame pending" flag in acks to data poll frames. In such a case,
435     // `IndirectSender` prepares and sends an empty frame to the child
436     // after it sends a data poll. Here in `HandleSentFrameToChild()` we
437     // exit quickly if we detect the "send done" is for the empty frame
438     // to ensure we do not update any newly added indirect message after
439     // preparing the empty frame.
440 
441     VerifyOrExit(nextOffset != 0);
442 
443     switch (aError)
444     {
445     case kErrorNone:
446         break;
447 
448     case kErrorNoAck:
449     case kErrorChannelAccessFailure:
450     case kErrorAbort:
451 
452         aChild.SetIndirectTxSuccess(false);
453 
454 #if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
455         // We set the nextOffset to end of message, since there is no need to
456         // send any remaining fragments in the message to the child, if all tx
457         // attempts of current frame already failed.
458 
459         if (message != nullptr)
460         {
461             nextOffset = message->GetLength();
462         }
463 #endif
464         break;
465 
466     default:
467         OT_ASSERT(false);
468         OT_UNREACHABLE_CODE(break);
469     }
470 
471     if ((message != nullptr) && (nextOffset < message->GetLength()))
472     {
473         aChild.SetIndirectFragmentOffset(nextOffset);
474         mDataPollHandler.HandleNewFrame(aChild);
475 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
476         mCslTxScheduler.Update();
477 #endif
478         ExitNow();
479     }
480 
481     if (message != nullptr)
482     {
483         // The indirect tx of this message to the child is done.
484 
485         Error        txError    = aError;
486         uint16_t     childIndex = Get<ChildTable>().GetChildIndex(aChild);
487         Mac::Address macDest;
488 
489         aChild.SetIndirectMessage(nullptr);
490         aChild.GetLinkInfo().AddMessageTxStatus(aChild.GetIndirectTxSuccess());
491 
492         // Enable short source address matching after the first indirect
493         // message transmission attempt to the child. We intentionally do
494         // not check for successful tx here to address the scenario where
495         // the child does receive "Child ID Response" but parent misses the
496         // 15.4 ack from child. If the "Child ID Response" does not make it
497         // to the child, then the child will need to send a new "Child ID
498         // Request" which will cause the parent to switch to using long
499         // address mode for source address matching.
500 
501         mSourceMatchController.SetSrcMatchAsShort(aChild, true);
502 
503 #if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
504 
505         // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
506         // disabled, all fragment frames of a larger message are
507         // sent even if the transmission of an earlier fragment fail.
508         // Note that `GetIndirectTxSuccess() tracks the tx success of
509         // the entire message to the child, while `txError = aError`
510         // represents the error status of the last fragment frame
511         // transmission.
512 
513         if (!aChild.GetIndirectTxSuccess() && (txError == kErrorNone))
514         {
515             txError = kErrorFailed;
516         }
517 #endif
518 
519         if (!aFrame.IsEmpty())
520         {
521             IgnoreError(aFrame.GetDstAddr(macDest));
522             Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *message, txError, &macDest);
523         }
524 
525         if (message->GetType() == Message::kTypeIp6)
526         {
527             if (aChild.GetIndirectTxSuccess())
528             {
529                 Get<MeshForwarder>().mIpCounters.mTxSuccess++;
530             }
531             else
532             {
533                 Get<MeshForwarder>().mIpCounters.mTxFailure++;
534             }
535         }
536 
537         if (message->GetChildMask(childIndex))
538         {
539             message->ClearChildMask(childIndex);
540             mSourceMatchController.DecrementMessageCount(aChild);
541         }
542 
543         Get<MeshForwarder>().RemoveMessageIfNoPendingTx(*message);
544     }
545 
546     UpdateIndirectMessage(aChild);
547 
548 exit:
549     if (mEnabled)
550     {
551         ClearMessagesForRemovedChildren();
552     }
553 }
554 
ClearMessagesForRemovedChildren(void)555 void IndirectSender::ClearMessagesForRemovedChildren(void)
556 {
557     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptValidOrRestoring))
558     {
559         if (child.GetIndirectMessageCount() == 0)
560         {
561             continue;
562         }
563 
564         ClearAllMessagesForSleepyChild(child);
565     }
566 }
567 
568 } // namespace ot
569 
570 #endif // #if OPENTHREAD_FTD
571