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