/src/openthread/src/core/thread/mesh_forwarder.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2016, 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 implements mesh forwarding of IPv6/6LoWPAN messages. |
32 | | */ |
33 | | |
34 | | #include "mesh_forwarder.hpp" |
35 | | |
36 | | #include "instance/instance.hpp" |
37 | | #include "utils/static_counter.hpp" |
38 | | |
39 | | namespace ot { |
40 | | |
41 | | RegisterLogModule("MeshForwarder"); |
42 | | |
43 | | void MeshForwarder::Counters::UpdateOnTxDone(const Message &aMessage, bool aTxSuccess) |
44 | 463k | { |
45 | 463k | if (aMessage.GetType() == Message::kTypeIp6) |
46 | 463k | { |
47 | 463k | aTxSuccess ? mTxSuccess++ : mTxFailure++; |
48 | 463k | } |
49 | 463k | } |
50 | | |
51 | | void MeshForwarder::Counters::UpdateOnRx(const Message &aMessage) |
52 | 3.83k | { |
53 | 3.83k | if (aMessage.GetType() == Message::kTypeIp6) |
54 | 3.83k | { |
55 | 3.83k | mRxSuccess++; |
56 | 3.83k | } |
57 | 3.83k | } |
58 | | |
59 | | void MeshForwarder::Counters::UpdateOnDrop(const Message &aMessage) |
60 | 18 | { |
61 | 18 | if (aMessage.GetType() == Message::kTypeIp6) |
62 | 18 | { |
63 | 18 | mRxFailure++; |
64 | 18 | } |
65 | 18 | } |
66 | | |
67 | | MeshForwarder::MeshForwarder(Instance &aInstance) |
68 | 5.14k | : InstanceLocator(aInstance) |
69 | 5.14k | , mMessageNextOffset(0) |
70 | 5.14k | , mSendMessage(nullptr) |
71 | | , mMeshSource() |
72 | | , mMeshDest() |
73 | 5.14k | , mAddMeshHeader(false) |
74 | 5.14k | , mEnabled(false) |
75 | 5.14k | , mTxPaused(false) |
76 | 5.14k | , mSendBusy(false) |
77 | | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE |
78 | 5.14k | , mDelayNextTx(false) |
79 | 5.14k | , mTxDelayTimer(aInstance) |
80 | | #endif |
81 | 5.14k | , mScheduleTransmissionTask(aInstance) |
82 | | #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE |
83 | 5.14k | , mIndirectSender(aInstance) |
84 | | #endif |
85 | 5.14k | , mDataPollSender(aInstance) |
86 | 5.14k | { |
87 | 5.14k | mFragTag = Random::NonCrypto::GetUint16(); |
88 | | |
89 | 5.14k | #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE |
90 | 5.14k | mTxQueueStats.Clear(); |
91 | 5.14k | #endif |
92 | 5.14k | } |
93 | | |
94 | | void MeshForwarder::Start(void) |
95 | 5.14k | { |
96 | 5.14k | if (!mEnabled) |
97 | 5.14k | { |
98 | 5.14k | Get<Mac::Mac>().SetRxOnWhenIdle(true); |
99 | 5.14k | #if OPENTHREAD_FTD |
100 | 5.14k | mIndirectSender.Start(); |
101 | 5.14k | #endif |
102 | | |
103 | 5.14k | mEnabled = true; |
104 | 5.14k | } |
105 | 5.14k | } |
106 | | |
107 | | void MeshForwarder::Stop(void) |
108 | 5.14k | { |
109 | 5.14k | VerifyOrExit(mEnabled); |
110 | | |
111 | 5.14k | mDataPollSender.StopPolling(); |
112 | 5.14k | Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMeshForwarder); |
113 | 5.14k | Get<Mle::DiscoverScanner>().Stop(); |
114 | | |
115 | 5.14k | mSendQueue.DequeueAndFreeAll(); |
116 | 5.14k | mReassemblyList.DequeueAndFreeAll(); |
117 | | |
118 | 5.14k | #if OPENTHREAD_FTD |
119 | 5.14k | mIndirectSender.Stop(); |
120 | 5.14k | mFwdFrameInfoArray.Clear(); |
121 | 5.14k | #endif |
122 | | |
123 | 5.14k | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE |
124 | 5.14k | mTxDelayTimer.Stop(); |
125 | 5.14k | mDelayNextTx = false; |
126 | 5.14k | #endif |
127 | | |
128 | 5.14k | mEnabled = false; |
129 | 5.14k | mSendMessage = nullptr; |
130 | 5.14k | Get<Mac::Mac>().SetRxOnWhenIdle(false); |
131 | | |
132 | 5.14k | exit: |
133 | 5.14k | return; |
134 | 5.14k | } |
135 | | |
136 | | void MeshForwarder::PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aMacDest, bool aAckRequest) |
137 | 0 | { |
138 | 0 | Mac::TxFrame::Info frameInfo; |
139 | |
|
140 | 0 | frameInfo.mAddrs.mSource.SetShort(Get<Mac::Mac>().GetShortAddress()); |
141 | |
|
142 | 0 | if (frameInfo.mAddrs.mSource.IsShortAddrInvalid() || aMacDest.IsExtended()) |
143 | 0 | { |
144 | 0 | frameInfo.mAddrs.mSource.SetExtended(Get<Mac::Mac>().GetExtAddress()); |
145 | 0 | } |
146 | |
|
147 | 0 | frameInfo.mAddrs.mDestination = aMacDest; |
148 | 0 | frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId()); |
149 | |
|
150 | 0 | frameInfo.mType = Mac::Frame::kTypeData; |
151 | 0 | frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; |
152 | 0 | frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1; |
153 | |
|
154 | 0 | PrepareMacHeaders(aFrame, frameInfo, nullptr); |
155 | |
|
156 | 0 | aFrame.SetAckRequest(aAckRequest); |
157 | 0 | aFrame.SetPayloadLength(0); |
158 | 0 | } |
159 | | |
160 | | void MeshForwarder::ResumeMessageTransmissions(void) |
161 | 0 | { |
162 | 0 | if (mTxPaused) |
163 | 0 | { |
164 | 0 | mTxPaused = false; |
165 | 0 | mScheduleTransmissionTask.Post(); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE |
170 | | void MeshForwarder::HandleTxDelayTimer(void) |
171 | 0 | { |
172 | 0 | mDelayNextTx = false; |
173 | 0 | mScheduleTransmissionTask.Post(); |
174 | 0 | LogDebg("Tx delay timer expired"); |
175 | 0 | } |
176 | | #endif |
177 | | |
178 | | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
179 | | |
180 | | Error MeshForwarder::UpdateEcnOrDrop(Message &aMessage, bool aPreparingToSend) |
181 | 4.86M | { |
182 | | // This method performs delay-aware active queue management for |
183 | | // direct message transmission. It parses the IPv6 header from |
184 | | // `aMessage` to determine if message is ECN-capable. This is |
185 | | // then used along with the message's time-in-queue to decide |
186 | | // whether to keep the message as is, change the ECN field to |
187 | | // mark congestion, or drop the message. If the message is to be |
188 | | // dropped, this method clears the direct tx flag on `aMessage` |
189 | | // and removes it from the send queue (if no pending indirect tx) |
190 | | // and returns `kErrorDrop`. This method returns `kErrorNone` |
191 | | // when the message is kept as is or ECN field is updated. |
192 | | |
193 | 4.86M | Error error = kErrorNone; |
194 | 4.86M | uint32_t timeInQueue = TimerMilli::GetNow() - aMessage.GetTimestamp(); |
195 | 4.86M | bool shouldMarkEcn = (timeInQueue >= kTimeInQueueMarkEcn); |
196 | 4.86M | bool isEcnCapable = false; |
197 | | |
198 | 4.86M | VerifyOrExit(aMessage.IsDirectTransmission() && (aMessage.GetOffset() == 0)); |
199 | | |
200 | 4.85M | if (aMessage.GetType() == Message::kTypeIp6) |
201 | 4.85M | { |
202 | 4.85M | Ip6::Header ip6Header; |
203 | | |
204 | 4.85M | IgnoreError(aMessage.Read(0, ip6Header)); |
205 | | |
206 | 4.85M | VerifyOrExit(!Get<ThreadNetif>().HasUnicastAddress(ip6Header.GetSource())); |
207 | | |
208 | 880 | isEcnCapable = (ip6Header.GetEcn() != Ip6::kEcnNotCapable); |
209 | | |
210 | 880 | if ((shouldMarkEcn && !isEcnCapable) || (timeInQueue >= kTimeInQueueDropMsg)) |
211 | 19 | { |
212 | 19 | ExitNow(error = kErrorDrop); |
213 | 19 | } |
214 | | |
215 | 861 | if (shouldMarkEcn) |
216 | 0 | { |
217 | 0 | switch (ip6Header.GetEcn()) |
218 | 0 | { |
219 | 0 | case Ip6::kEcnCapable0: |
220 | 0 | case Ip6::kEcnCapable1: |
221 | 0 | ip6Header.SetEcn(Ip6::kEcnMarked); |
222 | 0 | aMessage.Write(0, ip6Header); |
223 | 0 | LogMessage(kMessageMarkEcn, aMessage); |
224 | 0 | break; |
225 | | |
226 | 0 | case Ip6::kEcnMarked: |
227 | 0 | case Ip6::kEcnNotCapable: |
228 | 0 | break; |
229 | 0 | } |
230 | 0 | } |
231 | 861 | } |
232 | 91 | #if OPENTHREAD_FTD |
233 | 91 | else if (aMessage.GetType() == Message::kType6lowpan) |
234 | 91 | { |
235 | 91 | uint16_t headerLength = 0; |
236 | 91 | uint16_t offset; |
237 | 91 | bool hasFragmentHeader = false; |
238 | 91 | Lowpan::FragmentHeader fragmentHeader; |
239 | 91 | Lowpan::MeshHeader meshHeader; |
240 | | |
241 | 91 | IgnoreError(meshHeader.ParseFrom(aMessage, headerLength)); |
242 | | |
243 | 91 | offset = headerLength; |
244 | | |
245 | 91 | if (fragmentHeader.ParseFrom(aMessage, offset, headerLength) == kErrorNone) |
246 | 12 | { |
247 | 12 | hasFragmentHeader = true; |
248 | 12 | offset += headerLength; |
249 | 12 | } |
250 | | |
251 | 91 | if (!hasFragmentHeader || (fragmentHeader.GetDatagramOffset() == 0)) |
252 | 83 | { |
253 | 83 | Ip6::Ecn ecn = Get<Lowpan::Lowpan>().DecompressEcn(aMessage, offset); |
254 | | |
255 | 83 | isEcnCapable = (ecn != Ip6::kEcnNotCapable); |
256 | | |
257 | 83 | if ((shouldMarkEcn && !isEcnCapable) || (timeInQueue >= kTimeInQueueDropMsg)) |
258 | 0 | { |
259 | 0 | FwdFrameInfo *entry = FindFwdFrameInfoEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag()); |
260 | |
|
261 | 0 | if (entry != nullptr) |
262 | 0 | { |
263 | 0 | entry->MarkToDrop(); |
264 | 0 | entry->ResetLifetime(); |
265 | 0 | } |
266 | |
|
267 | 0 | ExitNow(error = kErrorDrop); |
268 | 0 | } |
269 | | |
270 | 83 | if (shouldMarkEcn) |
271 | 0 | { |
272 | 0 | switch (ecn) |
273 | 0 | { |
274 | 0 | case Ip6::kEcnCapable0: |
275 | 0 | case Ip6::kEcnCapable1: |
276 | 0 | Get<Lowpan::Lowpan>().MarkCompressedEcn(aMessage, offset); |
277 | 0 | LogMessage(kMessageMarkEcn, aMessage); |
278 | 0 | break; |
279 | | |
280 | 0 | case Ip6::kEcnMarked: |
281 | 0 | case Ip6::kEcnNotCapable: |
282 | 0 | break; |
283 | 0 | } |
284 | 0 | } |
285 | 83 | } |
286 | 8 | else if (hasFragmentHeader) |
287 | 8 | { |
288 | 8 | FwdFrameInfo *entry = FindFwdFrameInfoEntry(meshHeader.GetSource(), fragmentHeader.GetDatagramTag()); |
289 | | |
290 | 8 | VerifyOrExit(entry != nullptr); |
291 | | |
292 | 0 | if (entry->ShouldDrop()) |
293 | 0 | { |
294 | 0 | error = kErrorDrop; |
295 | 0 | } |
296 | | |
297 | | // We can remove the entry if it is the last fragment and |
298 | | // only if the message is being prepared to be sent out. |
299 | 0 | if (aPreparingToSend && (fragmentHeader.GetDatagramOffset() + aMessage.GetLength() - offset >= |
300 | 0 | fragmentHeader.GetDatagramSize())) |
301 | 0 | { |
302 | 0 | mFwdFrameInfoArray.Remove(*entry); |
303 | 0 | } |
304 | 0 | } |
305 | 91 | } |
306 | | #else |
307 | | OT_UNUSED_VARIABLE(aPreparingToSend); |
308 | | #endif // OPENTHREAD_FTD |
309 | | |
310 | 4.86M | exit: |
311 | 4.86M | if (error == kErrorDrop) |
312 | 19 | { |
313 | 19 | #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE |
314 | 19 | mTxQueueStats.UpdateFor(aMessage); |
315 | 19 | #endif |
316 | 19 | LogMessage(kMessageQueueMgmtDrop, aMessage); |
317 | 19 | FinalizeMessageDirectTx(aMessage, kErrorDrop); |
318 | 19 | RemoveMessageIfNoPendingTx(aMessage); |
319 | 19 | } |
320 | | |
321 | 4.86M | return error; |
322 | 4.85M | } |
323 | | |
324 | | Error MeshForwarder::RemoveAgedMessages(void) |
325 | 102k | { |
326 | | // This method goes through all messages in the send queue and |
327 | | // removes all aged messages determined based on the delay-aware |
328 | | // active queue management rules. It may also mark ECN on some |
329 | | // messages. It returns `kErrorNone` if at least one message was |
330 | | // removed, or `kErrorNotFound` if none was removed. |
331 | | |
332 | 102k | Error error = kErrorNotFound; |
333 | 102k | Message *nextMessage; |
334 | | |
335 | 4.59M | for (Message *message = mSendQueue.GetHead(); message != nullptr; message = nextMessage) |
336 | 4.49M | { |
337 | 4.49M | nextMessage = message->GetNext(); |
338 | | |
339 | | // Exclude the current message being sent `mSendMessage`. |
340 | 4.49M | if ((message == mSendMessage) || !message->IsDirectTransmission() || message->GetDoNotEvict()) |
341 | 102k | { |
342 | 102k | continue; |
343 | 102k | } |
344 | | |
345 | 4.39M | if (UpdateEcnOrDrop(*message, /* aPreparingToSend */ false) == kErrorDrop) |
346 | 0 | { |
347 | 0 | error = kErrorNone; |
348 | 0 | } |
349 | 4.39M | } |
350 | | |
351 | 102k | return error; |
352 | 102k | } |
353 | | |
354 | | #endif // OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
355 | | |
356 | | #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0) |
357 | | |
358 | | bool MeshForwarder::IsDirectTxQueueOverMaxFrameThreshold(void) const |
359 | 468k | { |
360 | 468k | uint16_t frameCount = 0; |
361 | | |
362 | 468k | for (const Message &message : mSendQueue) |
363 | 12.2M | { |
364 | 12.2M | if (!message.IsDirectTransmission() || (&message == mSendMessage)) |
365 | 365k | { |
366 | 365k | continue; |
367 | 365k | } |
368 | | |
369 | 11.8M | switch (message.GetType()) |
370 | 11.8M | { |
371 | 11.8M | case Message::kTypeIp6: |
372 | 11.8M | { |
373 | | // If it is an IPv6 message, we estimate the number of |
374 | | // fragment frames assuming typical header sizes and lowpan |
375 | | // compression. Since this estimate is only used for queue |
376 | | // management, we lean towards an under estimate in sense |
377 | | // that we may allow few more frames in the tx queue over |
378 | | // threshold in some rare cases. |
379 | | // |
380 | | // The constants below are derived as follows: Typical MAC |
381 | | // header (15 bytes) and MAC footer (6 bytes) leave 106 |
382 | | // bytes for MAC payload. Next fragment header is 5 bytes |
383 | | // leaving 96 for next fragment payload. Lowpan compression |
384 | | // on average compresses 40 bytes IPv6 header into about 19 |
385 | | // bytes leaving 87 bytes for the IPv6 payload, so the first |
386 | | // fragment can fit 87 + 40 = 127 bytes. |
387 | | |
388 | 11.8M | static constexpr uint16_t kFirstFragmentMaxLength = 127; |
389 | 11.8M | static constexpr uint16_t kNextFragmentSize = 96; |
390 | | |
391 | 11.8M | uint16_t length = message.GetLength(); |
392 | | |
393 | 11.8M | frameCount++; |
394 | | |
395 | 11.8M | if (length > kFirstFragmentMaxLength) |
396 | 1.80k | { |
397 | 1.80k | frameCount += (length - kFirstFragmentMaxLength) / kNextFragmentSize; |
398 | 1.80k | } |
399 | | |
400 | 11.8M | break; |
401 | 0 | } |
402 | | |
403 | 99 | case Message::kType6lowpan: |
404 | 99 | case Message::kTypeMacEmptyData: |
405 | 99 | frameCount++; |
406 | 99 | break; |
407 | | |
408 | 0 | case Message::kTypeSupervision: |
409 | 0 | default: |
410 | 0 | break; |
411 | 11.8M | } |
412 | 11.8M | } |
413 | | |
414 | 468k | return (frameCount > OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE); |
415 | 468k | } |
416 | | |
417 | | void MeshForwarder::ApplyDirectTxQueueLimit(Message &aMessage) |
418 | 468k | { |
419 | 468k | VerifyOrExit(aMessage.IsDirectTransmission()); |
420 | 468k | VerifyOrExit(IsDirectTxQueueOverMaxFrameThreshold()); |
421 | | |
422 | 0 | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
423 | 0 | { |
424 | 0 | bool originalEvictFlag = aMessage.GetDoNotEvict(); |
425 | 0 | Error error; |
426 | | |
427 | | // We mark the "do not evict" flag on the new `aMessage` so |
428 | | // that it will not be removed from `RemoveAgedMessages()`. |
429 | | // This protects against the unlikely case where the newly |
430 | | // queued `aMessage` may already be aged due to execution |
431 | | // being interrupted for a long time between the queuing of |
432 | | // the message and the `ApplyDirectTxQueueLimit()` call. We |
433 | | // do not want the message to be potentially removed and |
434 | | // freed twice. |
435 | |
|
436 | 0 | aMessage.SetDoNotEvict(true); |
437 | 0 | error = RemoveAgedMessages(); |
438 | 0 | aMessage.SetDoNotEvict(originalEvictFlag); |
439 | |
|
440 | 0 | if (error == kErrorNone) |
441 | 0 | { |
442 | 0 | VerifyOrExit(IsDirectTxQueueOverMaxFrameThreshold()); |
443 | 0 | } |
444 | 0 | } |
445 | 0 | #endif |
446 | | |
447 | 0 | LogMessage(kMessageFullQueueDrop, aMessage); |
448 | 0 | FinalizeMessageDirectTx(aMessage, kErrorDrop); |
449 | 0 | RemoveMessageIfNoPendingTx(aMessage); |
450 | |
|
451 | 468k | exit: |
452 | 468k | return; |
453 | 0 | } |
454 | | |
455 | | #endif // (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0) |
456 | | |
457 | | #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE |
458 | | const uint32_t *MeshForwarder::TxQueueStats::GetHistogram(uint16_t &aNumBins, uint32_t &aBinInterval) const |
459 | 0 | { |
460 | 0 | aNumBins = kNumHistBins; |
461 | 0 | aBinInterval = kHistBinInterval; |
462 | 0 | return mHistogram; |
463 | 0 | } |
464 | | |
465 | | void MeshForwarder::TxQueueStats::UpdateFor(const Message &aMessage) |
466 | 465k | { |
467 | 465k | uint32_t timeInQueue = TimerMilli::GetNow() - aMessage.GetTimestamp(); |
468 | | |
469 | 465k | mHistogram[Min<uint32_t>(timeInQueue / kHistBinInterval, kNumHistBins - 1)]++; |
470 | 465k | mMaxInterval = Max(mMaxInterval, timeInQueue); |
471 | 465k | } |
472 | | #endif |
473 | | |
474 | | void MeshForwarder::ScheduleTransmissionTask(void) |
475 | 930k | { |
476 | 930k | VerifyOrExit(!mSendBusy && !mTxPaused); |
477 | | |
478 | 565k | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE |
479 | 565k | VerifyOrExit(!mDelayNextTx); |
480 | 565k | #endif |
481 | | |
482 | 565k | mSendMessage = PrepareNextDirectTransmission(); |
483 | 565k | VerifyOrExit(mSendMessage != nullptr); |
484 | | |
485 | 464k | if (mSendMessage->GetOffset() == 0) |
486 | 462k | { |
487 | 462k | mSendMessage->SetTxSuccess(true); |
488 | 462k | } |
489 | | |
490 | 464k | Get<Mac::Mac>().RequestDirectFrameTransmission(); |
491 | | |
492 | 930k | exit: |
493 | 930k | return; |
494 | 464k | } |
495 | | |
496 | | Message *MeshForwarder::PrepareNextDirectTransmission(void) |
497 | 565k | { |
498 | 565k | Message *curMessage, *nextMessage; |
499 | 565k | Error error = kErrorNone; |
500 | | |
501 | 566k | for (curMessage = mSendQueue.GetHead(); curMessage; curMessage = nextMessage) |
502 | 465k | { |
503 | | // We set the `nextMessage` here but it can be updated again |
504 | | // after the `switch(message.GetType())` since it may be |
505 | | // evicted during message processing (e.g., from the call to |
506 | | // `UpdateIp6Route()` due to Address Solicit). |
507 | | |
508 | 465k | nextMessage = curMessage->GetNext(); |
509 | | |
510 | 465k | if (!curMessage->IsDirectTransmission() || curMessage->IsResolvingAddress()) |
511 | 0 | { |
512 | 0 | continue; |
513 | 0 | } |
514 | | |
515 | 465k | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
516 | 465k | if (UpdateEcnOrDrop(*curMessage, /* aPreparingToSend */ true) == kErrorDrop) |
517 | 19 | { |
518 | 19 | continue; |
519 | 19 | } |
520 | 465k | #endif |
521 | 465k | curMessage->SetDoNotEvict(true); |
522 | | |
523 | 465k | switch (curMessage->GetType()) |
524 | 465k | { |
525 | 465k | case Message::kTypeIp6: |
526 | 465k | error = UpdateIp6Route(*curMessage); |
527 | 465k | break; |
528 | | |
529 | 0 | #if OPENTHREAD_FTD |
530 | | |
531 | 91 | case Message::kType6lowpan: |
532 | 91 | error = UpdateMeshRoute(*curMessage); |
533 | 91 | break; |
534 | | |
535 | 0 | #endif |
536 | | |
537 | | #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
538 | | case Message::kTypeMacEmptyData: |
539 | | error = kErrorNone; |
540 | | break; |
541 | | #endif |
542 | | |
543 | 0 | default: |
544 | 0 | error = kErrorDrop; |
545 | 0 | break; |
546 | 465k | } |
547 | | |
548 | 465k | curMessage->SetDoNotEvict(false); |
549 | | |
550 | | // the next message may have been evicted during processing (e.g. due to Address Solicit) |
551 | 465k | nextMessage = curMessage->GetNext(); |
552 | | |
553 | 465k | switch (error) |
554 | 465k | { |
555 | 464k | case kErrorNone: |
556 | 464k | if (!curMessage->IsDirectTransmission()) |
557 | 0 | { |
558 | | // Skip if message is no longer marked for direct transmission. |
559 | | // For example, `UpdateIp6Route()` may determine the destination |
560 | | // is an ALOC associated with an SED child of this device and |
561 | | // mark it for indirect tx to the SED child. |
562 | 0 | continue; |
563 | 0 | } |
564 | | |
565 | 464k | #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE |
566 | 464k | mTxQueueStats.UpdateFor(*curMessage); |
567 | 464k | #endif |
568 | 464k | ExitNow(); |
569 | | |
570 | 0 | #if OPENTHREAD_FTD |
571 | 0 | case kErrorAddressQuery: |
572 | 0 | curMessage->SetResolvingAddress(true); |
573 | 0 | continue; |
574 | 0 | #endif |
575 | | |
576 | 819 | default: |
577 | 819 | #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE |
578 | 819 | mTxQueueStats.UpdateFor(*curMessage); |
579 | 819 | #endif |
580 | 819 | LogMessage(kMessageDrop, *curMessage, error); |
581 | 819 | FinalizeMessageDirectTx(*curMessage, error); |
582 | 819 | RemoveMessageIfNoPendingTx(*curMessage); |
583 | 819 | continue; |
584 | 465k | } |
585 | 465k | } |
586 | | |
587 | 565k | exit: |
588 | 565k | return curMessage; |
589 | 565k | } |
590 | | |
591 | | Error MeshForwarder::UpdateIp6Route(Message &aMessage) |
592 | 465k | { |
593 | 465k | Mle::Mle &mle = Get<Mle::Mle>(); |
594 | 465k | Error error = kErrorNone; |
595 | 465k | Ip6::Header ip6Header; |
596 | | |
597 | 465k | mAddMeshHeader = false; |
598 | | |
599 | 465k | IgnoreError(aMessage.Read(0, ip6Header)); |
600 | | |
601 | 465k | VerifyOrExit(!ip6Header.GetSource().IsMulticast(), error = kErrorDrop); |
602 | | |
603 | 465k | GetMacSourceAddress(ip6Header.GetSource(), mMacAddrs.mSource); |
604 | | |
605 | 465k | if (mle.IsDisabled() || mle.IsDetached()) |
606 | 465k | { |
607 | 465k | if (ip6Header.GetDestination().IsLinkLocalMulticast()) |
608 | 462k | { |
609 | 462k | mMacAddrs.mDestination.SetShort(Mac::kShortAddrBroadcast); |
610 | 462k | } |
611 | 2.99k | else if (ip6Header.GetDestination().IsLinkLocalUnicast()) |
612 | 2.26k | { |
613 | 2.26k | mMacAddrs.mDestination.SetExtendedFromIid(ip6Header.GetDestination().GetIid()); |
614 | 2.26k | } |
615 | 727 | else |
616 | 727 | { |
617 | 727 | error = kErrorDrop; |
618 | 727 | } |
619 | | |
620 | 465k | ExitNow(); |
621 | 465k | } |
622 | | |
623 | 0 | if (ip6Header.GetDestination().IsMulticast()) |
624 | 0 | { |
625 | | // With the exception of MLE multicasts and any other message |
626 | | // with link security disabled, an End Device transmits |
627 | | // multicasts, as IEEE 802.15.4 unicasts to its parent. |
628 | |
|
629 | 0 | if (mle.IsChild() && aMessage.IsLinkSecurityEnabled() && !aMessage.IsSubTypeMle()) |
630 | 0 | { |
631 | 0 | mMacAddrs.mDestination.SetShort(mle.GetParentRloc16()); |
632 | 0 | } |
633 | 0 | else |
634 | 0 | { |
635 | 0 | mMacAddrs.mDestination.SetShort(Mac::kShortAddrBroadcast); |
636 | 0 | } |
637 | 0 | } |
638 | 0 | else if (ip6Header.GetDestination().IsLinkLocalUnicast()) |
639 | 0 | { |
640 | 0 | mMacAddrs.mDestination.SetExtendedFromIid(ip6Header.GetDestination().GetIid()); |
641 | 0 | } |
642 | 0 | else if (mle.IsMinimalEndDevice()) |
643 | 0 | { |
644 | 0 | mMacAddrs.mDestination.SetShort(mle.GetParentRloc16()); |
645 | 0 | } |
646 | 0 | else |
647 | 0 | { |
648 | 0 | #if OPENTHREAD_FTD |
649 | 0 | error = UpdateIp6RouteFtd(ip6Header, aMessage); |
650 | | #else |
651 | | OT_ASSERT(false); |
652 | | #endif |
653 | 0 | } |
654 | |
|
655 | 465k | exit: |
656 | 465k | return error; |
657 | 0 | } |
658 | | |
659 | 29 | bool MeshForwarder::GetRxOnWhenIdle(void) const { return Get<Mac::Mac>().GetRxOnWhenIdle(); } |
660 | | |
661 | | void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle) |
662 | 65.3k | { |
663 | 65.3k | Get<Mac::Mac>().SetRxOnWhenIdle(aRxOnWhenIdle); |
664 | | |
665 | 65.3k | if (aRxOnWhenIdle) |
666 | 65.3k | { |
667 | 65.3k | mDataPollSender.StopPolling(); |
668 | 65.3k | Get<SupervisionListener>().Stop(); |
669 | 65.3k | } |
670 | 0 | else |
671 | 0 | { |
672 | 0 | mDataPollSender.StartPolling(); |
673 | 0 | Get<SupervisionListener>().Start(); |
674 | 0 | } |
675 | 65.3k | } |
676 | | |
677 | | void MeshForwarder::GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr) |
678 | 465k | { |
679 | 465k | aMacAddr.SetExtendedFromIid(aIp6Addr.GetIid()); |
680 | | |
681 | 465k | if (aMacAddr.GetExtended() != Get<Mac::Mac>().GetExtAddress()) |
682 | 2.09k | { |
683 | 2.09k | aMacAddr.SetShort(Get<Mac::Mac>().GetShortAddress()); |
684 | 2.09k | } |
685 | 465k | } |
686 | | |
687 | | Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames) |
688 | 464k | { |
689 | 464k | Mac::TxFrame *frame = nullptr; |
690 | 464k | bool addFragHeader = false; |
691 | | |
692 | 464k | VerifyOrExit(mEnabled && (mSendMessage != nullptr)); |
693 | | |
694 | | #if OPENTHREAD_CONFIG_MULTI_RADIO |
695 | | frame = &Get<RadioSelector>().SelectRadio(*mSendMessage, mMacAddrs.mDestination, aTxFrames); |
696 | | |
697 | | // If multi-radio link is supported, when sending frame with link |
698 | | // security enabled, Fragment Header is always included (even if |
699 | | // the message is small and does not require 6LoWPAN fragmentation). |
700 | | // This allows the Fragment Header's tag to be used to detect and |
701 | | // suppress duplicate received frames over different radio links. |
702 | | |
703 | | if (mSendMessage->IsLinkSecurityEnabled()) |
704 | | { |
705 | | addFragHeader = true; |
706 | | } |
707 | | #else |
708 | 464k | frame = &aTxFrames.GetTxFrame(); |
709 | 464k | #endif |
710 | | |
711 | 464k | mSendBusy = true; |
712 | | |
713 | 464k | switch (mSendMessage->GetType()) |
714 | 464k | { |
715 | 464k | case Message::kTypeIp6: |
716 | 464k | if (mSendMessage->IsMleCommand(Mle::kCommandDiscoveryRequest)) |
717 | 0 | { |
718 | 0 | frame = Get<Mle::DiscoverScanner>().PrepareDiscoveryRequestFrame(*frame); |
719 | 0 | VerifyOrExit(frame != nullptr); |
720 | 0 | } |
721 | | #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
722 | | else if (Get<Mac::Mac>().IsCslEnabled() && mSendMessage->IsSubTypeMle()) |
723 | | { |
724 | | mSendMessage->SetLinkSecurityEnabled(true); |
725 | | } |
726 | | #endif |
727 | 464k | mMessageNextOffset = |
728 | 464k | PrepareDataFrame(*frame, *mSendMessage, mMacAddrs, mAddMeshHeader, mMeshSource, mMeshDest, addFragHeader); |
729 | | |
730 | 464k | if (mSendMessage->IsMleCommand(Mle::kCommandChildIdRequest) && mSendMessage->IsLinkSecurityEnabled()) |
731 | 0 | { |
732 | 0 | LogNote("Child ID Request requires fragmentation, aborting tx"); |
733 | 0 | mMessageNextOffset = mSendMessage->GetLength(); |
734 | 0 | ExitNow(frame = nullptr); |
735 | 0 | } |
736 | 464k | break; |
737 | | |
738 | | #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
739 | | case Message::kTypeMacEmptyData: |
740 | | { |
741 | | Mac::Address macDestAddr; |
742 | | |
743 | | macDestAddr.SetShort(Get<Mle::Mle>().GetParent().GetRloc16()); |
744 | | PrepareEmptyFrame(*frame, macDestAddr, /* aAckRequest */ true); |
745 | | } |
746 | | break; |
747 | | #endif |
748 | | |
749 | 464k | #if OPENTHREAD_FTD |
750 | | |
751 | 464k | case Message::kType6lowpan: |
752 | 0 | SendMesh(*mSendMessage, *frame); |
753 | 0 | break; |
754 | | |
755 | 0 | case Message::kTypeSupervision: |
756 | | // A direct supervision message is possible in the case where |
757 | | // a sleepy child switches its mode (becomes non-sleepy) while |
758 | | // there is a pending indirect supervision message in the send |
759 | | // queue for it. The message would be then converted to a |
760 | | // direct tx. |
761 | |
|
762 | 0 | OT_FALL_THROUGH; |
763 | 0 | #endif |
764 | |
|
765 | 0 | default: |
766 | 0 | mMessageNextOffset = mSendMessage->GetLength(); |
767 | 0 | ExitNow(frame = nullptr); |
768 | 464k | } |
769 | | |
770 | 464k | frame->SetIsARetransmission(false); |
771 | | |
772 | 464k | exit: |
773 | 464k | return frame; |
774 | 464k | } |
775 | | |
776 | | void MeshForwarder::PrepareMacHeaders(Mac::TxFrame &aTxFrame, Mac::TxFrame::Info &aTxFrameInfo, const Message *aMessage) |
777 | 464k | { |
778 | 464k | const Neighbor *neighbor; |
779 | | |
780 | 464k | aTxFrameInfo.mVersion = Mac::Frame::kVersion2006; |
781 | | |
782 | 464k | #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
783 | | |
784 | 464k | #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || \ |
785 | 464k | OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE |
786 | | |
787 | | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
788 | | // Determine frame version and Header IE entries |
789 | | |
790 | 464k | neighbor = Get<NeighborTable>().FindNeighbor(aTxFrameInfo.mAddrs.mDestination); |
791 | | |
792 | 464k | if (neighbor == nullptr) |
793 | 464k | { |
794 | 464k | } |
795 | | #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
796 | | else if (Get<Mac::Mac>().IsCslEnabled()) |
797 | | { |
798 | | aTxFrameInfo.mAppendCslIe = true; |
799 | | aTxFrameInfo.mVersion = Mac::Frame::kVersion2015; |
800 | | } |
801 | | #endif |
802 | 0 | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE |
803 | 0 | else if ((Get<ChildTable>().Contains(*neighbor) && static_cast<const Child *>(neighbor)->IsCslSynchronized())) |
804 | 0 | { |
805 | 0 | aTxFrameInfo.mVersion = Mac::Frame::kVersion2015; |
806 | 0 | } |
807 | 464k | #endif |
808 | | #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE |
809 | | else if (neighbor->IsEnhAckProbingActive()) |
810 | | { |
811 | | aTxFrameInfo.mVersion = Mac::Frame::kVersion2015; |
812 | | } |
813 | | #endif |
814 | | |
815 | 464k | #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
816 | | // || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE |
817 | | |
818 | | #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
819 | | if ((aMessage != nullptr) && aMessage->IsTimeSync()) |
820 | | { |
821 | | aTxFrameInfo.mAppendTimeIe = true; |
822 | | aTxFrameInfo.mVersion = Mac::Frame::kVersion2015; |
823 | | } |
824 | | #endif |
825 | | |
826 | 464k | aTxFrameInfo.mEmptyPayload = (aMessage == nullptr) || (aMessage->GetLength() == 0); |
827 | | |
828 | 464k | #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
829 | | |
830 | | //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
831 | | // Prepare MAC headers |
832 | | |
833 | 464k | aTxFrameInfo.PrepareHeadersIn(aTxFrame); |
834 | | |
835 | 464k | OT_UNUSED_VARIABLE(aMessage); |
836 | 464k | OT_UNUSED_VARIABLE(neighbor); |
837 | 464k | } |
838 | | |
839 | | // This method constructs a MAC data from from a given IPv6 message. |
840 | | // |
841 | | // This method handles generation of MAC header, mesh header (if |
842 | | // requested), lowpan compression of IPv6 header, lowpan fragmentation |
843 | | // header (if message requires fragmentation or if it is explicitly |
844 | | // requested by setting `aAddFragHeader` to `true`) It uses the |
845 | | // message offset to construct next fragments. This method enables |
846 | | // link security when message is MLE type and requires fragmentation. |
847 | | // It returns the next offset into the message after the prepared |
848 | | // frame. |
849 | | // |
850 | | uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame &aFrame, |
851 | | Message &aMessage, |
852 | | const Mac::Addresses &aMacAddrs, |
853 | | bool aAddMeshHeader, |
854 | | uint16_t aMeshSource, |
855 | | uint16_t aMeshDest, |
856 | | bool aAddFragHeader) |
857 | 464k | { |
858 | 464k | Mac::TxFrame::Info frameInfo; |
859 | 464k | uint16_t payloadLength; |
860 | 464k | uint16_t origMsgOffset; |
861 | 464k | uint16_t nextOffset; |
862 | 464k | FrameBuilder frameBuilder; |
863 | | |
864 | 464k | start: |
865 | 464k | frameInfo.Clear(); |
866 | | |
867 | 464k | if (aMessage.IsLinkSecurityEnabled()) |
868 | 392k | { |
869 | 392k | frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; |
870 | | |
871 | 392k | if (aMessage.GetSubType() == Message::kSubTypeJoinerEntrust) |
872 | 0 | { |
873 | 0 | frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode0; |
874 | 0 | } |
875 | 392k | else if (aMessage.IsMleCommand(Mle::kCommandAnnounce)) |
876 | 389k | { |
877 | 389k | frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode2; |
878 | 389k | } |
879 | 3.60k | else |
880 | 3.60k | { |
881 | 3.60k | frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1; |
882 | 3.60k | } |
883 | 392k | } |
884 | | |
885 | 464k | frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId()); |
886 | | |
887 | 464k | if (aMessage.IsSubTypeMle()) |
888 | 460k | { |
889 | 460k | switch (aMessage.GetMleCommand()) |
890 | 460k | { |
891 | 389k | case Mle::kCommandAnnounce: |
892 | 389k | aFrame.SetChannel(aMessage.GetChannel()); |
893 | 389k | aFrame.SetRxChannelAfterTxDone(Get<Mac::Mac>().GetPanChannel()); |
894 | 389k | frameInfo.mPanIds.SetDestination(Mac::kPanIdBroadcast); |
895 | 389k | break; |
896 | | |
897 | 0 | case Mle::kCommandDiscoveryRequest: |
898 | 65 | case Mle::kCommandDiscoveryResponse: |
899 | 65 | frameInfo.mPanIds.SetDestination(aMessage.GetPanId()); |
900 | 65 | break; |
901 | | |
902 | 71.8k | default: |
903 | 71.8k | break; |
904 | 460k | } |
905 | 460k | } |
906 | | |
907 | 464k | frameInfo.mType = Mac::Frame::kTypeData; |
908 | 464k | frameInfo.mAddrs = aMacAddrs; |
909 | | |
910 | 464k | PrepareMacHeaders(aFrame, frameInfo, &aMessage); |
911 | | |
912 | 464k | frameBuilder.Init(aFrame.GetPayload(), aFrame.GetMaxPayloadLength()); |
913 | | |
914 | 464k | #if OPENTHREAD_FTD |
915 | | |
916 | | // Initialize Mesh header |
917 | 464k | if (aAddMeshHeader) |
918 | 0 | { |
919 | 0 | Lowpan::MeshHeader meshHeader; |
920 | 0 | uint16_t maxPayloadLength; |
921 | | |
922 | | // Mesh Header frames are forwarded by routers over multiple |
923 | | // hops to reach a final destination. The forwarding path can |
924 | | // have routers supporting different radio links with varying |
925 | | // MTU sizes. Since the originator of the frame does not know the |
926 | | // path and the MTU sizes of supported radio links by the routers |
927 | | // in the path, we limit the max payload length of a Mesh Header |
928 | | // frame to a fixed minimum value (derived from 15.4 radio) |
929 | | // ensuring it can be handled by any radio link. |
930 | | // |
931 | | // Maximum payload length is calculated by subtracting the frame |
932 | | // header and footer lengths from the MTU size. The footer |
933 | | // length is derived by removing the `aFrame.GetFcsSize()` and |
934 | | // then adding the fixed `kMeshHeaderFrameFcsSize` instead |
935 | | // (updating the FCS size in the calculation of footer length). |
936 | |
|
937 | 0 | maxPayloadLength = kMeshHeaderFrameMtu - aFrame.GetHeaderLength() - |
938 | 0 | (aFrame.GetFooterLength() - aFrame.GetFcsSize() + kMeshHeaderFrameFcsSize); |
939 | |
|
940 | 0 | frameBuilder.Init(aFrame.GetPayload(), maxPayloadLength); |
941 | |
|
942 | 0 | meshHeader.Init(aMeshSource, aMeshDest, kMeshHeaderHopsLeft); |
943 | |
|
944 | 0 | IgnoreError(meshHeader.AppendTo(frameBuilder)); |
945 | 0 | } |
946 | | |
947 | 464k | #endif // OPENTHREAD_FTD |
948 | | |
949 | | // While performing lowpan compression, the message offset may be |
950 | | // changed to skip over the compressed IPv6 headers, we save the |
951 | | // original offset and set it back on `aMessage` at the end |
952 | | // before returning. |
953 | | |
954 | 464k | origMsgOffset = aMessage.GetOffset(); |
955 | | |
956 | | // Compress IPv6 Header |
957 | 464k | if (aMessage.GetOffset() == 0) |
958 | 462k | { |
959 | 462k | uint16_t fragHeaderOffset; |
960 | 462k | uint16_t maxFrameLength; |
961 | 462k | Mac::Addresses macAddrs; |
962 | | |
963 | | // Before performing lowpan header compression, we reduce the |
964 | | // max length on `frameBuilder` to reserve bytes for first |
965 | | // fragment header. This ensures that lowpan compression will |
966 | | // leave room for a first fragment header. After the lowpan |
967 | | // header compression is done, we reclaim the reserved bytes |
968 | | // by setting the max length back to its original value. |
969 | | |
970 | 462k | fragHeaderOffset = frameBuilder.GetLength(); |
971 | 462k | maxFrameLength = frameBuilder.GetMaxLength(); |
972 | 462k | frameBuilder.SetMaxLength(maxFrameLength - sizeof(Lowpan::FragmentHeader::FirstFrag)); |
973 | | |
974 | 462k | if (aAddMeshHeader) |
975 | 0 | { |
976 | 0 | macAddrs.mSource.SetShort(aMeshSource); |
977 | 0 | macAddrs.mDestination.SetShort(aMeshDest); |
978 | 0 | } |
979 | 462k | else |
980 | 462k | { |
981 | 462k | macAddrs = aMacAddrs; |
982 | 462k | } |
983 | | |
984 | 462k | SuccessOrAssert(Get<Lowpan::Lowpan>().Compress(aMessage, macAddrs, frameBuilder)); |
985 | | |
986 | 462k | frameBuilder.SetMaxLength(maxFrameLength); |
987 | | |
988 | 462k | payloadLength = aMessage.GetLength() - aMessage.GetOffset(); |
989 | | |
990 | 462k | if (aAddFragHeader || (payloadLength > frameBuilder.GetRemainingLength())) |
991 | 718 | { |
992 | 718 | Lowpan::FragmentHeader::FirstFrag firstFragHeader; |
993 | | |
994 | 718 | if ((!aMessage.IsLinkSecurityEnabled()) && aMessage.IsSubTypeMle()) |
995 | 0 | { |
996 | | // MLE messages that require fragmentation MUST use |
997 | | // link-layer security. We enable security and try |
998 | | // constructing the frame again. |
999 | |
|
1000 | 0 | aMessage.SetOffset(0); |
1001 | 0 | aMessage.SetLinkSecurityEnabled(true); |
1002 | 0 | goto start; |
1003 | 0 | } |
1004 | | |
1005 | | // Insert Fragment header |
1006 | 718 | if (aMessage.GetDatagramTag() == 0) |
1007 | 718 | { |
1008 | | // Avoid using datagram tag value 0, which indicates the tag has not been set |
1009 | 718 | if (mFragTag == 0) |
1010 | 0 | { |
1011 | 0 | mFragTag++; |
1012 | 0 | } |
1013 | | |
1014 | 718 | aMessage.SetDatagramTag(mFragTag++); |
1015 | 718 | } |
1016 | | |
1017 | 718 | firstFragHeader.Init(aMessage.GetLength(), static_cast<uint16_t>(aMessage.GetDatagramTag())); |
1018 | 718 | SuccessOrAssert(frameBuilder.Insert(fragHeaderOffset, firstFragHeader)); |
1019 | 718 | } |
1020 | 462k | } |
1021 | 1.97k | else |
1022 | 1.97k | { |
1023 | 1.97k | Lowpan::FragmentHeader::NextFrag nextFragHeader; |
1024 | | |
1025 | 1.97k | nextFragHeader.Init(aMessage.GetLength(), static_cast<uint16_t>(aMessage.GetDatagramTag()), |
1026 | 1.97k | aMessage.GetOffset()); |
1027 | 1.97k | SuccessOrAssert(frameBuilder.Append(nextFragHeader)); |
1028 | | |
1029 | 1.97k | payloadLength = aMessage.GetLength() - aMessage.GetOffset(); |
1030 | 1.97k | } |
1031 | | |
1032 | 464k | if (payloadLength > frameBuilder.GetRemainingLength()) |
1033 | 2.12k | { |
1034 | 2.12k | payloadLength = (frameBuilder.GetRemainingLength() & ~0x7); |
1035 | 2.12k | } |
1036 | | |
1037 | | // Copy IPv6 Payload |
1038 | 464k | SuccessOrAssert(frameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), payloadLength)); |
1039 | 464k | aFrame.SetPayloadLength(frameBuilder.GetLength()); |
1040 | | |
1041 | 464k | nextOffset = aMessage.GetOffset() + payloadLength; |
1042 | | |
1043 | 464k | if (nextOffset < aMessage.GetLength()) |
1044 | 2.12k | { |
1045 | 2.12k | aFrame.SetFramePending(true); |
1046 | | #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
1047 | | aMessage.SetTimeSync(false); |
1048 | | #endif |
1049 | 2.12k | } |
1050 | | |
1051 | 464k | aMessage.SetOffset(origMsgOffset); |
1052 | | |
1053 | 464k | return nextOffset; |
1054 | 464k | } |
1055 | | |
1056 | | uint16_t MeshForwarder::PrepareDataFrameWithNoMeshHeader(Mac::TxFrame &aFrame, |
1057 | | Message &aMessage, |
1058 | | const Mac::Addresses &aMacAddrs) |
1059 | 0 | { |
1060 | 0 | return PrepareDataFrame(aFrame, aMessage, aMacAddrs, /* aAddMeshHeader */ false, /* aMeshSource */ 0xffff, |
1061 | 0 | /* aMeshDest */ 0xffff, /* aAddFragHeader */ false); |
1062 | 0 | } |
1063 | | |
1064 | | Neighbor *MeshForwarder::UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, |
1065 | | Error aError, |
1066 | | const Mac::Address &aMacDest, |
1067 | | bool aIsDataPoll) |
1068 | 464k | { |
1069 | 464k | OT_UNUSED_VARIABLE(aIsDataPoll); |
1070 | | |
1071 | 464k | Neighbor *neighbor = nullptr; |
1072 | 464k | uint8_t failLimit = kFailedRouterTransmissions; |
1073 | | |
1074 | 464k | VerifyOrExit(mEnabled); |
1075 | | |
1076 | 464k | neighbor = Get<NeighborTable>().FindNeighbor(aMacDest); |
1077 | 464k | VerifyOrExit(neighbor != nullptr); |
1078 | | |
1079 | 0 | VerifyOrExit(aFrame.GetAckRequest()); |
1080 | | |
1081 | | #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE |
1082 | | // TREL radio link uses deferred ack model. We ignore |
1083 | | // `SendDone` event from `Mac` layer with success status and |
1084 | | // wait for deferred ack callback instead. |
1085 | | #if OPENTHREAD_CONFIG_MULTI_RADIO |
1086 | | if (aFrame.GetRadioType() == Mac::kRadioTypeTrel) |
1087 | | #endif |
1088 | | { |
1089 | | VerifyOrExit(aError != kErrorNone); |
1090 | | } |
1091 | | #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE |
1092 | | |
1093 | | #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
1094 | | if (aFrame.HasCslIe() && aIsDataPoll) |
1095 | | { |
1096 | | failLimit = kFailedCslDataPollTransmissions; |
1097 | | } |
1098 | | #endif |
1099 | | |
1100 | 0 | UpdateNeighborLinkFailures(*neighbor, aError, /* aAllowNeighborRemove */ true, failLimit); |
1101 | |
|
1102 | 464k | exit: |
1103 | 464k | return neighbor; |
1104 | 0 | } |
1105 | | |
1106 | | void MeshForwarder::UpdateNeighborLinkFailures(Neighbor &aNeighbor, |
1107 | | Error aError, |
1108 | | bool aAllowNeighborRemove, |
1109 | | uint8_t aFailLimit) |
1110 | 0 | { |
1111 | | // Update neighbor `LinkFailures` counter on ack error. |
1112 | |
|
1113 | 0 | if (aError == kErrorNone) |
1114 | 0 | { |
1115 | 0 | aNeighbor.ResetLinkFailures(); |
1116 | 0 | } |
1117 | 0 | else if (aError == kErrorNoAck) |
1118 | 0 | { |
1119 | 0 | aNeighbor.IncrementLinkFailures(); |
1120 | |
|
1121 | 0 | if (aAllowNeighborRemove && (Mle::IsRouterRloc16(aNeighbor.GetRloc16())) && |
1122 | 0 | (aNeighbor.GetLinkFailures() >= aFailLimit)) |
1123 | 0 | { |
1124 | 0 | #if OPENTHREAD_FTD |
1125 | 0 | Get<Mle::Mle>().RemoveRouterLink(static_cast<Router &>(aNeighbor)); |
1126 | | #else |
1127 | | IgnoreError(Get<Mle::Mle>().BecomeDetached()); |
1128 | | #endif |
1129 | 0 | } |
1130 | 0 | } |
1131 | 0 | } |
1132 | | |
1133 | | #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE |
1134 | | void MeshForwarder::HandleDeferredAck(Neighbor &aNeighbor, Error aError) |
1135 | | { |
1136 | | bool allowNeighborRemove = true; |
1137 | | |
1138 | | VerifyOrExit(mEnabled); |
1139 | | |
1140 | | if (aError == kErrorNoAck) |
1141 | | { |
1142 | | LogInfo("Deferred ack timeout on trel for neighbor %s rloc16:0x%04x", |
1143 | | aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16()); |
1144 | | } |
1145 | | |
1146 | | #if OPENTHREAD_CONFIG_MULTI_RADIO |
1147 | | // In multi radio mode, `RadioSelector` will update the neighbor's |
1148 | | // link failure counter and removes the neighbor if required. |
1149 | | Get<RadioSelector>().UpdateOnDeferredAck(aNeighbor, aError, allowNeighborRemove); |
1150 | | #else |
1151 | | UpdateNeighborLinkFailures(aNeighbor, aError, allowNeighborRemove, kFailedRouterTransmissions); |
1152 | | #endif |
1153 | | |
1154 | | exit: |
1155 | | return; |
1156 | | } |
1157 | | #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE |
1158 | | |
1159 | | void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, Error aError) |
1160 | 464k | { |
1161 | 464k | Neighbor *neighbor = nullptr; |
1162 | 464k | Mac::Address macDest; |
1163 | | |
1164 | 464k | OT_ASSERT((aError == kErrorNone) || (aError == kErrorChannelAccessFailure) || (aError == kErrorAbort) || |
1165 | 464k | (aError == kErrorNoAck)); |
1166 | | |
1167 | 464k | mSendBusy = false; |
1168 | | |
1169 | 464k | VerifyOrExit(mEnabled); |
1170 | | |
1171 | 464k | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE |
1172 | 464k | if (mDelayNextTx && (aError == kErrorNone)) |
1173 | 0 | { |
1174 | 0 | mTxDelayTimer.Start(kTxDelayInterval); |
1175 | 0 | LogDebg("Start tx delay timer for %lu msec", ToUlong(kTxDelayInterval)); |
1176 | 0 | } |
1177 | 464k | else |
1178 | 464k | { |
1179 | 464k | mDelayNextTx = false; |
1180 | 464k | } |
1181 | 464k | #endif |
1182 | | |
1183 | 464k | if (!aFrame.IsEmpty()) |
1184 | 464k | { |
1185 | 464k | IgnoreError(aFrame.GetDstAddr(macDest)); |
1186 | 464k | neighbor = UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ false); |
1187 | 464k | } |
1188 | | |
1189 | 464k | UpdateSendMessage(aError, macDest, neighbor); |
1190 | | |
1191 | 464k | exit: |
1192 | 464k | return; |
1193 | 464k | } |
1194 | | |
1195 | | void MeshForwarder::UpdateSendMessage(Error aFrameTxError, Mac::Address &aMacDest, Neighbor *aNeighbor) |
1196 | 464k | { |
1197 | 464k | Error txError = aFrameTxError; |
1198 | | |
1199 | 464k | VerifyOrExit(mSendMessage != nullptr); |
1200 | | |
1201 | 464k | OT_ASSERT(mSendMessage->IsDirectTransmission()); |
1202 | | |
1203 | 464k | if (aFrameTxError != kErrorNone) |
1204 | 0 | { |
1205 | | // If the transmission of any fragment frame fails, |
1206 | | // the overall message transmission is considered |
1207 | | // as failed |
1208 | |
|
1209 | 0 | mSendMessage->SetTxSuccess(false); |
1210 | |
|
1211 | 0 | #if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE |
1212 | | |
1213 | | // We set the NextOffset to end of message to avoid sending |
1214 | | // any remaining fragments in the message. |
1215 | |
|
1216 | 0 | mMessageNextOffset = mSendMessage->GetLength(); |
1217 | 0 | #endif |
1218 | 0 | } |
1219 | | |
1220 | 464k | if (mMessageNextOffset < mSendMessage->GetLength()) |
1221 | 2.12k | { |
1222 | 2.12k | mSendMessage->SetOffset(mMessageNextOffset); |
1223 | 2.12k | ExitNow(); |
1224 | 2.12k | } |
1225 | | |
1226 | 462k | txError = aFrameTxError; |
1227 | | |
1228 | 462k | if (aNeighbor != nullptr) |
1229 | 0 | { |
1230 | 0 | aNeighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess()); |
1231 | 0 | } |
1232 | | |
1233 | | #if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE |
1234 | | |
1235 | | // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is |
1236 | | // disabled, all fragment frames of a larger message are |
1237 | | // sent even if the transmission of an earlier fragment fail. |
1238 | | // Note that `GetTxSuccess() tracks the tx success of the |
1239 | | // entire message, while `aFrameTxError` represents the error |
1240 | | // status of the last fragment frame transmission. |
1241 | | |
1242 | | if (!mSendMessage->GetTxSuccess() && (txError == kErrorNone)) |
1243 | | { |
1244 | | txError = kErrorFailed; |
1245 | | } |
1246 | | #endif |
1247 | | |
1248 | 462k | #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE |
1249 | 462k | Get<Utils::HistoryTracker>().RecordTxMessage(*mSendMessage, aMacDest); |
1250 | 462k | #endif |
1251 | | |
1252 | 462k | LogMessage(kMessageTransmit, *mSendMessage, txError, &aMacDest); |
1253 | 462k | FinalizeMessageDirectTx(*mSendMessage, txError); |
1254 | 462k | RemoveMessageIfNoPendingTx(*mSendMessage); |
1255 | | |
1256 | 464k | exit: |
1257 | 464k | mScheduleTransmissionTask.Post(); |
1258 | 464k | } |
1259 | | |
1260 | | void MeshForwarder::FinalizeMessageDirectTx(Message &aMessage, Error aError) |
1261 | 463k | { |
1262 | | // Finalizes the direct transmission of `aMessage`. This can be |
1263 | | // triggered by successful delivery (all fragments reaching the |
1264 | | // destination), failure of any fragment, queue management |
1265 | | // dropping the message, or eviction of message to accommodate |
1266 | | // higher priority messages. |
1267 | | |
1268 | 463k | VerifyOrExit(aMessage.IsDirectTransmission()); |
1269 | | |
1270 | 463k | aMessage.ClearDirectTransmission(); |
1271 | 463k | aMessage.SetOffset(0); |
1272 | | |
1273 | 463k | if (aError != kErrorNone) |
1274 | 844 | { |
1275 | 844 | aMessage.SetTxSuccess(false); |
1276 | 844 | } |
1277 | | |
1278 | 463k | mCounters.UpdateOnTxDone(aMessage, aMessage.GetTxSuccess()); |
1279 | | |
1280 | 463k | if (aMessage.IsMleCommand(Mle::kCommandDiscoveryRequest)) |
1281 | 0 | { |
1282 | | // Note that `HandleDiscoveryRequestFrameTxDone()` may update |
1283 | | // `aMessage` and mark it again for direct transmission. |
1284 | 0 | Get<Mle::DiscoverScanner>().HandleDiscoveryRequestFrameTxDone(aMessage, aError); |
1285 | 0 | } |
1286 | 463k | else if (aMessage.IsMleCommand(Mle::kCommandChildIdRequest)) |
1287 | 0 | { |
1288 | 0 | Get<Mle::Mle>().HandleChildIdRequestTxDone(aMessage); |
1289 | 0 | } |
1290 | | |
1291 | 463k | exit: |
1292 | 463k | return; |
1293 | 463k | } |
1294 | | |
1295 | | void MeshForwarder::FinalizeAndRemoveMessage(Message &aMessage, Error aError, MessageAction aAction) |
1296 | 6 | { |
1297 | 6 | LogMessage(aAction, aMessage, aError); |
1298 | | |
1299 | 6 | #if OPENTHREAD_FTD |
1300 | 6 | FinalizeMessageIndirectTxs(aMessage); |
1301 | 6 | #endif |
1302 | | |
1303 | 6 | FinalizeMessageDirectTx(aMessage, aError); |
1304 | 6 | RemoveMessageIfNoPendingTx(aMessage); |
1305 | 6 | } |
1306 | | |
1307 | | bool MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage) |
1308 | 931k | { |
1309 | 931k | bool didRemove = false; |
1310 | | |
1311 | 931k | #if OPENTHREAD_FTD |
1312 | 931k | VerifyOrExit(!aMessage.IsDirectTransmission() && aMessage.GetIndirectTxChildMask().IsEmpty()); |
1313 | | #else |
1314 | | VerifyOrExit(!aMessage.IsDirectTransmission()); |
1315 | | #endif |
1316 | | |
1317 | 463k | if (mSendMessage == &aMessage) |
1318 | 462k | { |
1319 | 462k | mSendMessage = nullptr; |
1320 | 462k | mMessageNextOffset = 0; |
1321 | 462k | } |
1322 | | |
1323 | 463k | mSendQueue.DequeueAndFree(aMessage); |
1324 | 463k | didRemove = true; |
1325 | | |
1326 | 931k | exit: |
1327 | 931k | return didRemove; |
1328 | 463k | } |
1329 | | |
1330 | | Error MeshForwarder::RxInfo::ParseIp6Headers(void) |
1331 | 5.22k | { |
1332 | 5.22k | Error error = kErrorNone; |
1333 | | |
1334 | 5.22k | VerifyOrExit(!mParsedIp6Headers); |
1335 | 4.97k | SuccessOrExit(error = mIp6Headers.DecompressFrom(mFrameData, mMacAddrs, GetInstance())); |
1336 | 4.37k | mParsedIp6Headers = true; |
1337 | | |
1338 | 5.22k | exit: |
1339 | 5.22k | return error; |
1340 | 4.37k | } |
1341 | | |
1342 | | void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) |
1343 | 4.81k | { |
1344 | 4.81k | Error error = kErrorNone; |
1345 | 4.81k | RxInfo rxInfo(GetInstance()); |
1346 | | |
1347 | 4.81k | VerifyOrExit(mEnabled, error = kErrorInvalidState); |
1348 | | |
1349 | 4.81k | rxInfo.mFrameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength()); |
1350 | | |
1351 | 4.81k | SuccessOrExit(error = aFrame.GetSrcAddr(rxInfo.mMacAddrs.mSource)); |
1352 | 4.81k | SuccessOrExit(error = aFrame.GetDstAddr(rxInfo.mMacAddrs.mDestination)); |
1353 | | |
1354 | 4.81k | rxInfo.mLinkInfo.SetFrom(aFrame); |
1355 | | |
1356 | 4.81k | Get<SupervisionListener>().UpdateOnReceive(rxInfo.mMacAddrs.mSource, rxInfo.IsLinkSecurityEnabled()); |
1357 | | |
1358 | 4.81k | switch (aFrame.GetType()) |
1359 | 4.81k | { |
1360 | 4.74k | case Mac::Frame::kTypeData: |
1361 | 4.74k | if (Lowpan::MeshHeader::IsMeshHeader(rxInfo.mFrameData)) |
1362 | 366 | { |
1363 | 366 | #if OPENTHREAD_FTD |
1364 | 366 | HandleMesh(rxInfo); |
1365 | 366 | #endif |
1366 | 366 | } |
1367 | 4.38k | else if (Lowpan::FragmentHeader::IsFragmentHeader(rxInfo.mFrameData)) |
1368 | 81 | { |
1369 | 81 | HandleFragment(rxInfo); |
1370 | 81 | } |
1371 | 4.29k | else if (Lowpan::Lowpan::IsLowpanHc(rxInfo.mFrameData)) |
1372 | 4.23k | { |
1373 | 4.23k | HandleLowpanHc(rxInfo); |
1374 | 4.23k | } |
1375 | 68 | else |
1376 | 68 | { |
1377 | 68 | VerifyOrExit(rxInfo.mFrameData.GetLength() == 0, error = kErrorNotLowpanDataFrame); |
1378 | | |
1379 | 59 | LogFrame("Received empty payload frame", aFrame, kErrorNone); |
1380 | 59 | } |
1381 | | |
1382 | 4.73k | break; |
1383 | | |
1384 | 4.73k | case Mac::Frame::kTypeBeacon: |
1385 | 18 | break; |
1386 | | |
1387 | 49 | default: |
1388 | 49 | error = kErrorDrop; |
1389 | 49 | break; |
1390 | 4.81k | } |
1391 | | |
1392 | 4.81k | exit: |
1393 | | |
1394 | 4.81k | if (error != kErrorNone) |
1395 | 58 | { |
1396 | 58 | LogFrame("Dropping rx frame", aFrame, error); |
1397 | 58 | } |
1398 | 4.81k | } |
1399 | | |
1400 | | void MeshForwarder::HandleFragment(RxInfo &aRxInfo) |
1401 | 86 | { |
1402 | 86 | Error error = kErrorNone; |
1403 | 86 | Lowpan::FragmentHeader fragmentHeader; |
1404 | 86 | Message *message = nullptr; |
1405 | | |
1406 | 86 | SuccessOrExit(error = fragmentHeader.ParseFrom(aRxInfo.mFrameData)); |
1407 | | |
1408 | | #if OPENTHREAD_CONFIG_MULTI_RADIO |
1409 | | |
1410 | | if (aRxInfo.IsLinkSecurityEnabled()) |
1411 | | { |
1412 | | Neighbor *neighbor = |
1413 | | Get<NeighborTable>().FindNeighbor(aRxInfo.GetSrcAddr(), Neighbor::kInStateAnyExceptInvalid); |
1414 | | |
1415 | | if ((neighbor != nullptr) && (fragmentHeader.GetDatagramOffset() == 0)) |
1416 | | { |
1417 | | uint16_t tag = fragmentHeader.GetDatagramTag(); |
1418 | | |
1419 | | if (neighbor->IsLastRxFragmentTagSet()) |
1420 | | { |
1421 | | VerifyOrExit(!neighbor->IsLastRxFragmentTagAfter(tag), error = kErrorDuplicated); |
1422 | | } |
1423 | | |
1424 | | neighbor->SetLastRxFragmentTag(tag); |
1425 | | } |
1426 | | |
1427 | | // Duplication suppression for a "next fragment" is handled |
1428 | | // by the code below where the the datagram offset is |
1429 | | // checked against the offset of the corresponding message |
1430 | | // (same datagram tag and size) in Reassembly List. Note |
1431 | | // that if there is no matching message in the Reassembly |
1432 | | // List (e.g., in case the message is already fully |
1433 | | // assembled) the received "next fragment" frame would be |
1434 | | // dropped. |
1435 | | } |
1436 | | |
1437 | | #endif // OPENTHREAD_CONFIG_MULTI_RADIO |
1438 | | |
1439 | 83 | if (fragmentHeader.GetDatagramOffset() == 0) |
1440 | 78 | { |
1441 | 78 | uint16_t datagramSize = fragmentHeader.GetDatagramSize(); |
1442 | | |
1443 | 78 | #if OPENTHREAD_FTD |
1444 | 78 | UpdateEidRlocCacheAndStaleChild(aRxInfo); |
1445 | 78 | #endif |
1446 | | |
1447 | 78 | SuccessOrExit(error = FrameToMessage(aRxInfo, datagramSize, message)); |
1448 | | |
1449 | 61 | VerifyOrExit(datagramSize >= message->GetLength(), error = kErrorParse); |
1450 | 57 | SuccessOrExit(error = message->SetLength(datagramSize)); |
1451 | | |
1452 | 57 | message->SetDatagramTag(fragmentHeader.GetDatagramTag()); |
1453 | 57 | message->SetTimestampToNow(); |
1454 | 57 | message->UpdateLinkInfoFrom(aRxInfo.mLinkInfo); |
1455 | | |
1456 | 57 | VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop); |
1457 | | |
1458 | 24 | #if OPENTHREAD_FTD |
1459 | 24 | CheckReachabilityToSendIcmpError(*message, aRxInfo.mMacAddrs); |
1460 | 24 | #endif |
1461 | | |
1462 | | // Allow re-assembly of only one message at a time on a SED by clearing |
1463 | | // any remaining fragments in reassembly list upon receiving of a new |
1464 | | // (secure) first fragment. |
1465 | | |
1466 | 24 | if (!GetRxOnWhenIdle() && message->IsLinkSecurityEnabled()) |
1467 | 0 | { |
1468 | 0 | ClearReassemblyList(); |
1469 | 0 | } |
1470 | | |
1471 | 24 | mReassemblyList.Enqueue(*message); |
1472 | | |
1473 | 24 | Get<TimeTicker>().RegisterReceiver(TimeTicker::kMeshForwarder); |
1474 | 24 | } |
1475 | 5 | else // Received frame is a "next fragment". |
1476 | 5 | { |
1477 | 5 | for (Message &msg : mReassemblyList) |
1478 | 0 | { |
1479 | | // Security Check: only consider reassembly buffers that had the same Security Enabled setting. |
1480 | 0 | if (msg.GetLength() == fragmentHeader.GetDatagramSize() && |
1481 | 0 | msg.GetDatagramTag() == fragmentHeader.GetDatagramTag() && |
1482 | 0 | msg.GetOffset() == fragmentHeader.GetDatagramOffset() && |
1483 | 0 | msg.GetOffset() + aRxInfo.mFrameData.GetLength() <= fragmentHeader.GetDatagramSize() && |
1484 | 0 | msg.IsLinkSecurityEnabled() == aRxInfo.IsLinkSecurityEnabled()) |
1485 | 0 | { |
1486 | 0 | message = &msg; |
1487 | 0 | break; |
1488 | 0 | } |
1489 | 0 | } |
1490 | | |
1491 | | // For a sleepy-end-device, if we receive a new (secure) next fragment |
1492 | | // with a non-matching fragmentation offset or tag, it indicates that |
1493 | | // we have either missed a fragment, or the parent has moved to a new |
1494 | | // message with a new tag. In either case, we can safely clear any |
1495 | | // remaining fragments stored in the reassembly list. |
1496 | | |
1497 | 5 | if (!GetRxOnWhenIdle() && (message == nullptr) && aRxInfo.IsLinkSecurityEnabled()) |
1498 | 0 | { |
1499 | 0 | ClearReassemblyList(); |
1500 | 0 | } |
1501 | | |
1502 | 5 | VerifyOrExit(message != nullptr, error = kErrorDrop); |
1503 | | |
1504 | 0 | message->WriteData(message->GetOffset(), aRxInfo.mFrameData); |
1505 | 0 | message->MoveOffset(aRxInfo.mFrameData.GetLength()); |
1506 | 0 | message->AddRss(aRxInfo.mLinkInfo.GetRss()); |
1507 | 0 | message->AddLqi(aRxInfo.mLinkInfo.GetLqi()); |
1508 | 0 | message->SetTimestampToNow(); |
1509 | 0 | } |
1510 | | |
1511 | 86 | exit: |
1512 | | |
1513 | 86 | if (error == kErrorNone) |
1514 | 24 | { |
1515 | 24 | if (message->GetOffset() >= message->GetLength()) |
1516 | 6 | { |
1517 | 6 | mReassemblyList.Dequeue(*message); |
1518 | 6 | IgnoreError(HandleDatagram(*message, aRxInfo.GetSrcAddr())); |
1519 | 6 | } |
1520 | 24 | } |
1521 | 62 | else |
1522 | 62 | { |
1523 | 62 | LogFragmentFrameDrop(error, aRxInfo, fragmentHeader); |
1524 | 62 | FreeMessage(message); |
1525 | 62 | } |
1526 | 86 | } |
1527 | | |
1528 | | void MeshForwarder::ClearReassemblyList(void) |
1529 | 0 | { |
1530 | 0 | for (Message &message : mReassemblyList) |
1531 | 0 | { |
1532 | 0 | LogMessage(kMessageReassemblyDrop, message, kErrorNoFrameReceived); |
1533 | 0 | mCounters.UpdateOnDrop(message); |
1534 | 0 | mReassemblyList.DequeueAndFree(message); |
1535 | 0 | } |
1536 | 0 | } |
1537 | | |
1538 | | void MeshForwarder::HandleTimeTick(void) |
1539 | 71 | { |
1540 | 71 | bool continueRxingTicks = false; |
1541 | | |
1542 | 71 | #if OPENTHREAD_FTD |
1543 | 71 | continueRxingTicks = UpdateFwdFrameInfoArrayOnTimeTick(); |
1544 | 71 | #endif |
1545 | | |
1546 | 71 | continueRxingTicks = UpdateReassemblyList() || continueRxingTicks; |
1547 | | |
1548 | 71 | if (!continueRxingTicks) |
1549 | 24 | { |
1550 | 24 | Get<TimeTicker>().UnregisterReceiver(TimeTicker::kMeshForwarder); |
1551 | 24 | } |
1552 | 71 | } |
1553 | | |
1554 | | bool MeshForwarder::UpdateReassemblyList(void) |
1555 | 71 | { |
1556 | 71 | TimeMilli now = TimerMilli::GetNow(); |
1557 | | |
1558 | 71 | for (Message &message : mReassemblyList) |
1559 | 54 | { |
1560 | 54 | if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kReassemblyTimeout)) |
1561 | 18 | { |
1562 | 18 | LogMessage(kMessageReassemblyDrop, message, kErrorReassemblyTimeout); |
1563 | 18 | mCounters.UpdateOnDrop(message); |
1564 | 18 | mReassemblyList.DequeueAndFree(message); |
1565 | 18 | } |
1566 | 54 | } |
1567 | | |
1568 | 71 | return mReassemblyList.GetHead() != nullptr; |
1569 | 71 | } |
1570 | | |
1571 | | Error MeshForwarder::FrameToMessage(RxInfo &aRxInfo, uint16_t aDatagramSize, Message *&aMessage) |
1572 | 4.43k | { |
1573 | 4.43k | Error error = kErrorNone; |
1574 | 4.43k | FrameData frameData = aRxInfo.mFrameData; |
1575 | 4.43k | Message::Priority priority; |
1576 | | |
1577 | 4.43k | SuccessOrExit(error = GetFramePriority(aRxInfo, priority)); |
1578 | | |
1579 | 4.28k | aMessage = Get<MessagePool>().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, Message::Settings(priority)); |
1580 | 4.28k | VerifyOrExit(aMessage, error = kErrorNoBufs); |
1581 | | |
1582 | 4.28k | SuccessOrExit(error = Get<Lowpan::Lowpan>().Decompress(*aMessage, aRxInfo.mMacAddrs, frameData, aDatagramSize)); |
1583 | | |
1584 | 4.07k | SuccessOrExit(error = aMessage->AppendData(frameData)); |
1585 | 4.07k | aMessage->MoveOffset(frameData.GetLength()); |
1586 | | |
1587 | 4.43k | exit: |
1588 | 4.43k | return error; |
1589 | 4.07k | } |
1590 | | |
1591 | | void MeshForwarder::HandleLowpanHc(RxInfo &aRxInfo) |
1592 | 4.35k | { |
1593 | 4.35k | Error error = kErrorNone; |
1594 | 4.35k | Message *message = nullptr; |
1595 | | |
1596 | 4.35k | #if OPENTHREAD_FTD |
1597 | 4.35k | UpdateEidRlocCacheAndStaleChild(aRxInfo); |
1598 | 4.35k | #endif |
1599 | | |
1600 | 4.35k | SuccessOrExit(error = FrameToMessage(aRxInfo, 0, message)); |
1601 | | |
1602 | 4.00k | message->UpdateLinkInfoFrom(aRxInfo.mLinkInfo); |
1603 | | |
1604 | 4.00k | VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop); |
1605 | | |
1606 | 3.82k | #if OPENTHREAD_FTD |
1607 | 3.82k | CheckReachabilityToSendIcmpError(*message, aRxInfo.mMacAddrs); |
1608 | 3.82k | #endif |
1609 | | |
1610 | 4.35k | exit: |
1611 | | |
1612 | 4.35k | if (error == kErrorNone) |
1613 | 3.82k | { |
1614 | 3.82k | IgnoreError(HandleDatagram(*message, aRxInfo.GetSrcAddr())); |
1615 | 3.82k | } |
1616 | 527 | else |
1617 | 527 | { |
1618 | 527 | LogLowpanHcFrameDrop(error, aRxInfo); |
1619 | 527 | FreeMessage(message); |
1620 | 527 | } |
1621 | 4.35k | } |
1622 | | |
1623 | | Error MeshForwarder::HandleDatagram(Message &aMessage, const Mac::Address &aMacSource) |
1624 | 3.83k | { |
1625 | 3.83k | #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE |
1626 | 3.83k | Get<Utils::HistoryTracker>().RecordRxMessage(aMessage, aMacSource); |
1627 | 3.83k | #endif |
1628 | | |
1629 | 3.83k | LogMessage(kMessageReceive, aMessage, kErrorNone, &aMacSource); |
1630 | | |
1631 | 3.83k | mCounters.UpdateOnRx(aMessage); |
1632 | | |
1633 | 3.83k | aMessage.SetLoopbackToHostAllowed(true); |
1634 | 3.83k | aMessage.SetOrigin(Message::kOriginThreadNetif); |
1635 | | |
1636 | 3.83k | return Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&aMessage)); |
1637 | 3.83k | } |
1638 | | |
1639 | | Error MeshForwarder::GetFramePriority(RxInfo &aRxInfo, Message::Priority &aPriority) |
1640 | 4.51k | { |
1641 | 4.51k | Error error = kErrorNone; |
1642 | | |
1643 | 4.51k | SuccessOrExit(error = aRxInfo.ParseIp6Headers()); |
1644 | | |
1645 | 4.28k | aPriority = Ip6::Ip6::DscpToPriority(aRxInfo.mIp6Headers.GetIp6Header().GetDscp()); |
1646 | | |
1647 | | // Only ICMPv6 error messages are prioritized. |
1648 | 4.28k | if (aRxInfo.mIp6Headers.IsIcmp6() && aRxInfo.mIp6Headers.GetIcmpHeader().IsError()) |
1649 | 7 | { |
1650 | 7 | aPriority = Message::kPriorityNet; |
1651 | 7 | } |
1652 | | |
1653 | 4.28k | if (aRxInfo.mIp6Headers.IsUdp()) |
1654 | 2.87k | { |
1655 | 2.87k | uint16_t destPort = aRxInfo.mIp6Headers.GetUdpHeader().GetDestinationPort(); |
1656 | | |
1657 | 2.87k | if (destPort == Mle::kUdpPort) |
1658 | 952 | { |
1659 | 952 | aPriority = Message::kPriorityNet; |
1660 | 952 | } |
1661 | 1.92k | else if (Get<Tmf::Agent>().IsTmfMessage(aRxInfo.mIp6Headers.GetSourceAddress(), |
1662 | 1.92k | aRxInfo.mIp6Headers.GetDestinationAddress(), destPort)) |
1663 | 1.18k | { |
1664 | 1.18k | aPriority = Tmf::Agent::DscpToPriority(aRxInfo.mIp6Headers.GetIp6Header().GetDscp()); |
1665 | 1.18k | } |
1666 | 2.87k | } |
1667 | | |
1668 | 4.51k | exit: |
1669 | 4.51k | return error; |
1670 | 4.28k | } |
1671 | | |
1672 | | #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
1673 | | Error MeshForwarder::SendEmptyMessage(void) |
1674 | | { |
1675 | | Error error = kErrorNone; |
1676 | | OwnedPtr<Message> messagePtr; |
1677 | | |
1678 | | VerifyOrExit(mEnabled && !Get<Mac::Mac>().GetRxOnWhenIdle() && |
1679 | | Get<Mle::Mle>().GetParent().IsStateValidOrRestoring(), |
1680 | | error = kErrorInvalidState); |
1681 | | |
1682 | | messagePtr.Reset(Get<MessagePool>().Allocate(Message::kTypeMacEmptyData)); |
1683 | | VerifyOrExit(messagePtr != nullptr, error = kErrorNoBufs); |
1684 | | |
1685 | | SendMessage(messagePtr.PassOwnership()); |
1686 | | |
1687 | | exit: |
1688 | | LogDebg("Send empty message, error:%s", ErrorToString(error)); |
1689 | | return error; |
1690 | | } |
1691 | | #endif |
1692 | | |
1693 | | // LCOV_EXCL_START |
1694 | | |
1695 | | #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) |
1696 | | |
1697 | | const char *MeshForwarder::MessageActionToString(MessageAction aAction, Error aError) |
1698 | | { |
1699 | | static const char *const kMessageActionStrings[] = { |
1700 | | "Received", // (0) kMessageReceive |
1701 | | "Sent", // (1) kMessageTransmit |
1702 | | "Prepping indir tx", // (2) kMessagePrepareIndirect |
1703 | | "Dropping", // (3) kMessageDrop |
1704 | | "Dropping (reassembly queue)", // (4) kMessageReassemblyDrop |
1705 | | "Evicting", // (5) kMessageEvict |
1706 | | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
1707 | | "Marked ECN", // (6) kMessageMarkEcn |
1708 | | "Dropping (queue mgmt)", // (7) kMessageQueueMgmtDrop |
1709 | | #endif |
1710 | | #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0) |
1711 | | "Dropping (dir queue full)", // (8) kMessageFullQueueDrop |
1712 | | #endif |
1713 | | }; |
1714 | | |
1715 | | const char *string = kMessageActionStrings[aAction]; |
1716 | | |
1717 | | struct MessageActionChecker |
1718 | | { |
1719 | | InitEnumValidatorCounter(); |
1720 | | |
1721 | | ValidateNextEnum(kMessageReceive); |
1722 | | ValidateNextEnum(kMessageTransmit); |
1723 | | ValidateNextEnum(kMessagePrepareIndirect); |
1724 | | ValidateNextEnum(kMessageDrop); |
1725 | | ValidateNextEnum(kMessageReassemblyDrop); |
1726 | | ValidateNextEnum(kMessageEvict); |
1727 | | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
1728 | | ValidateNextEnum(kMessageMarkEcn); |
1729 | | ValidateNextEnum(kMessageQueueMgmtDrop); |
1730 | | #endif |
1731 | | #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0) |
1732 | | ValidateNextEnum(kMessageFullQueueDrop); |
1733 | | #endif |
1734 | | }; |
1735 | | |
1736 | | if ((aAction == kMessageTransmit) && (aError != kErrorNone)) |
1737 | | { |
1738 | | string = "Failed to send"; |
1739 | | } |
1740 | | |
1741 | | return string; |
1742 | | } |
1743 | | |
1744 | | const char *MeshForwarder::MessagePriorityToString(const Message &aMessage) |
1745 | | { |
1746 | | return Message::PriorityToString(aMessage.GetPriority()); |
1747 | | } |
1748 | | |
1749 | | #if OPENTHREAD_CONFIG_LOG_SRC_DST_IP_ADDRESSES |
1750 | | void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &aHeaders, LogLevel aLogLevel) |
1751 | | { |
1752 | | LogIp6AddressAndPort("src", aHeaders.GetSourceAddress(), aHeaders.GetSourcePort(), aLogLevel); |
1753 | | LogIp6AddressAndPort("dst", aHeaders.GetDestinationAddress(), aHeaders.GetDestinationPort(), aLogLevel); |
1754 | | } |
1755 | | |
1756 | | void MeshForwarder::LogIp6AddressAndPort(const char *aLabel, |
1757 | | const Ip6::Address &aAddress, |
1758 | | uint16_t aPort, |
1759 | | LogLevel aLogLevel) |
1760 | | { |
1761 | | Ip6::SockAddr::InfoString string; |
1762 | | |
1763 | | string.Append("[%s]", aAddress.ToString().AsCString()); |
1764 | | |
1765 | | if (aPort != 0) |
1766 | | { |
1767 | | string.Append(":%u", aPort); |
1768 | | } |
1769 | | |
1770 | | LogAt(aLogLevel, " %s:%s", aLabel, string.AsCString()); |
1771 | | } |
1772 | | |
1773 | | #else |
1774 | | void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &, LogLevel) {} |
1775 | | #endif |
1776 | | |
1777 | | void MeshForwarder::LogIp6Message(MessageAction aAction, |
1778 | | const Message &aMessage, |
1779 | | const Mac::Address *aMacAddress, |
1780 | | Error aError, |
1781 | | LogLevel aLogLevel) |
1782 | | { |
1783 | | Ip6::Headers headers; |
1784 | | String<kMaxLogStringSize> string; |
1785 | | |
1786 | | SuccessOrExit(headers.ParseFrom(aMessage)); |
1787 | | |
1788 | | string.Append("%s IPv6 %s msg, len:%u, chksum:%04x, ecn:%s, ", MessageActionToString(aAction, aError), |
1789 | | Ip6::Ip6::IpProtoToString(headers.GetIpProto()), aMessage.GetLength(), headers.GetChecksum(), |
1790 | | Ip6::Ip6::EcnToString(headers.GetEcn())); |
1791 | | |
1792 | | AppendMacAddrToLogString(string, aAction, aMacAddress); |
1793 | | AppendSecErrorPrioRssRadioLabelsToLogString(string, aAction, aMessage, aError); |
1794 | | |
1795 | | LogAt(aLogLevel, "%s", string.AsCString()); |
1796 | | |
1797 | | if (aAction != kMessagePrepareIndirect) |
1798 | | { |
1799 | | LogIp6SourceDestAddresses(headers, aLogLevel); |
1800 | | } |
1801 | | |
1802 | | exit: |
1803 | | return; |
1804 | | } |
1805 | | |
1806 | | void MeshForwarder::AppendMacAddrToLogString(StringWriter &aString, |
1807 | | MessageAction aAction, |
1808 | | const Mac::Address *aMacAddress) |
1809 | | { |
1810 | | VerifyOrExit(aMacAddress != nullptr); |
1811 | | |
1812 | | if (aAction == kMessageReceive) |
1813 | | { |
1814 | | aString.Append("from:"); |
1815 | | } |
1816 | | else |
1817 | | { |
1818 | | aString.Append("to:"); |
1819 | | } |
1820 | | |
1821 | | aString.Append("%s, ", aMacAddress->ToString().AsCString()); |
1822 | | |
1823 | | exit: |
1824 | | return; |
1825 | | } |
1826 | | |
1827 | | void MeshForwarder::AppendSecErrorPrioRssRadioLabelsToLogString(StringWriter &aString, |
1828 | | MessageAction aAction, |
1829 | | const Message &aMessage, |
1830 | | Error aError) |
1831 | | { |
1832 | | aString.Append("sec:%s, ", ToYesNo(aMessage.IsLinkSecurityEnabled())); |
1833 | | |
1834 | | if (aError != kErrorNone) |
1835 | | { |
1836 | | aString.Append("error:%s, ", ErrorToString(aError)); |
1837 | | } |
1838 | | |
1839 | | aString.Append("prio:%s", MessagePriorityToString(aMessage)); |
1840 | | |
1841 | | if ((aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop)) |
1842 | | { |
1843 | | aString.Append(", rss:%s", aMessage.GetRssAverager().ToString().AsCString()); |
1844 | | } |
1845 | | |
1846 | | #if OPENTHREAD_CONFIG_MULTI_RADIO |
1847 | | aString.Append(", radio:%s", aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all"); |
1848 | | #endif |
1849 | | } |
1850 | | |
1851 | | void MeshForwarder::LogMessage(MessageAction aAction, const Message &aMessage) |
1852 | | { |
1853 | | LogMessage(aAction, aMessage, kErrorNone); |
1854 | | } |
1855 | | |
1856 | | void MeshForwarder::LogMessage(MessageAction aAction, const Message &aMessage, Error aError) |
1857 | | { |
1858 | | LogMessage(aAction, aMessage, aError, nullptr); |
1859 | | } |
1860 | | |
1861 | | void MeshForwarder::LogMessage(MessageAction aAction, |
1862 | | const Message &aMessage, |
1863 | | Error aError, |
1864 | | const Mac::Address *aMacAddress) |
1865 | | |
1866 | | { |
1867 | | LogLevel logLevel = kLogLevelInfo; |
1868 | | |
1869 | | switch (aAction) |
1870 | | { |
1871 | | case kMessageReceive: |
1872 | | case kMessageTransmit: |
1873 | | case kMessagePrepareIndirect: |
1874 | | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
1875 | | case kMessageMarkEcn: |
1876 | | #endif |
1877 | | logLevel = (aError == kErrorNone) ? kLogLevelInfo : kLogLevelNote; |
1878 | | break; |
1879 | | |
1880 | | case kMessageDrop: |
1881 | | case kMessageReassemblyDrop: |
1882 | | case kMessageEvict: |
1883 | | #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE |
1884 | | case kMessageQueueMgmtDrop: |
1885 | | #endif |
1886 | | #if (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0) |
1887 | | case kMessageFullQueueDrop: |
1888 | | #endif |
1889 | | logLevel = kLogLevelNote; |
1890 | | break; |
1891 | | } |
1892 | | |
1893 | | VerifyOrExit(Instance::GetLogLevel() >= logLevel); |
1894 | | |
1895 | | switch (aMessage.GetType()) |
1896 | | { |
1897 | | case Message::kTypeIp6: |
1898 | | LogIp6Message(aAction, aMessage, aMacAddress, aError, logLevel); |
1899 | | break; |
1900 | | |
1901 | | #if OPENTHREAD_FTD |
1902 | | case Message::kType6lowpan: |
1903 | | LogMeshMessage(aAction, aMessage, aMacAddress, aError, logLevel); |
1904 | | break; |
1905 | | #endif |
1906 | | |
1907 | | default: |
1908 | | break; |
1909 | | } |
1910 | | |
1911 | | exit: |
1912 | | return; |
1913 | | } |
1914 | | |
1915 | | void MeshForwarder::LogFrame(const char *aActionText, const Mac::Frame &aFrame, Error aError) |
1916 | | { |
1917 | | if (aError != kErrorNone) |
1918 | | { |
1919 | | LogNote("%s, aError:%s, %s", aActionText, ErrorToString(aError), aFrame.ToInfoString().AsCString()); |
1920 | | } |
1921 | | else |
1922 | | { |
1923 | | LogInfo("%s, %s", aActionText, aFrame.ToInfoString().AsCString()); |
1924 | | } |
1925 | | } |
1926 | | |
1927 | | void MeshForwarder::LogFragmentFrameDrop(Error aError, |
1928 | | const RxInfo &aRxInfo, |
1929 | | const Lowpan::FragmentHeader &aFragmentHeader) |
1930 | | { |
1931 | | LogNote("Dropping rx frag frame, error:%s, %s, tag:%d, offset:%d, dglen:%d", ErrorToString(aError), |
1932 | | aRxInfo.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(), |
1933 | | aFragmentHeader.GetDatagramSize()); |
1934 | | } |
1935 | | |
1936 | | void MeshForwarder::LogLowpanHcFrameDrop(Error aError, const RxInfo &aRxInfo) |
1937 | | { |
1938 | | LogNote("Dropping rx lowpan HC frame, error:%s, %s", ErrorToString(aError), aRxInfo.ToString().AsCString()); |
1939 | | } |
1940 | | |
1941 | | MeshForwarder::RxInfo::InfoString MeshForwarder::RxInfo::ToString(void) const |
1942 | | { |
1943 | | InfoString string; |
1944 | | |
1945 | | string.Append("len:%d, src:%s, dst:%s, sec:%s", mFrameData.GetLength(), GetSrcAddr().ToString().AsCString(), |
1946 | | GetDstAddr().ToString().AsCString(), ToYesNo(IsLinkSecurityEnabled())); |
1947 | | |
1948 | | return string; |
1949 | | } |
1950 | | |
1951 | | #else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) |
1952 | | |
1953 | 738 | void MeshForwarder::LogMessage(MessageAction, const Message &) {} |
1954 | | |
1955 | 843 | void MeshForwarder::LogMessage(MessageAction, const Message &, Error) {} |
1956 | | |
1957 | 466k | void MeshForwarder::LogMessage(MessageAction, const Message &, Error, const Mac::Address *) {} |
1958 | | |
1959 | 117 | void MeshForwarder::LogFrame(const char *, const Mac::Frame &, Error) {} |
1960 | | |
1961 | 62 | void MeshForwarder::LogFragmentFrameDrop(Error, const RxInfo &, const Lowpan::FragmentHeader &) {} |
1962 | | |
1963 | 527 | void MeshForwarder::LogLowpanHcFrameDrop(Error, const RxInfo &) {} |
1964 | | |
1965 | | #endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) |
1966 | | |
1967 | | // LCOV_EXCL_STOP |
1968 | | |
1969 | | } // namespace ot |