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