| /* |
| * Copyright (c) 2018, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements FTD-specific mesh forwarding of IPv6/6LoWPAN messages. |
| */ |
| |
| #if OPENTHREAD_FTD |
| |
| #define WPP_NAME "mesh_forwarder_ftd.tmh" |
| |
| #include "mesh_forwarder.hpp" |
| |
| #include "common/logging.hpp" |
| #include "common/owner-locator.hpp" |
| #include "meshcop/meshcop.hpp" |
| #include "net/ip6.hpp" |
| #include "net/tcp.hpp" |
| #include "net/udp6.hpp" |
| |
| namespace ot { |
| |
| otError MeshForwarder::SendMessage(Message &aMessage) |
| { |
| ThreadNetif &netif = GetNetif(); |
| ChildTable & childTable = netif.GetMle().GetChildTable(); |
| otError error = OT_ERROR_NONE; |
| Neighbor * neighbor; |
| |
| switch (aMessage.GetType()) |
| { |
| case Message::kTypeIp6: |
| { |
| Ip6::Header ip6Header; |
| |
| aMessage.Read(0, sizeof(ip6Header), &ip6Header); |
| |
| if (ip6Header.GetDestination().IsMulticast()) |
| { |
| // For traffic destined to multicast address larger than realm local, generally it uses IP-in-IP |
| // encapsulation (RFC2473), with outer destination as ALL_MPL_FORWARDERS. So here if the destination |
| // is multicast address larger than realm local, it should be for indirection transmission for the |
| // device's sleepy child, thus there should be no direct transmission. |
| if (!ip6Header.GetDestination().IsMulticastLargerThanRealmLocal()) |
| { |
| // schedule direct transmission |
| aMessage.SetDirectTransmission(); |
| } |
| |
| if (aMessage.GetSubType() != Message::kSubTypeMplRetransmission) |
| { |
| if (ip6Header.GetDestination() == netif.GetMle().GetLinkLocalAllThreadNodesAddress() || |
| ip6Header.GetDestination() == netif.GetMle().GetRealmLocalAllThreadNodesAddress()) |
| { |
| // destined for all sleepy children |
| for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); |
| iter++) |
| { |
| Child &child = *iter.GetChild(); |
| |
| if (!child.IsRxOnWhenIdle()) |
| { |
| aMessage.SetChildMask(childTable.GetChildIndex(child)); |
| mSourceMatchController.IncrementMessageCount(child); |
| } |
| } |
| } |
| else |
| { |
| // destined for some sleepy children which subscribed the multicast address. |
| for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); |
| iter++) |
| { |
| Child &child = *iter.GetChild(); |
| |
| if (netif.GetMle().IsSleepyChildSubscribed(ip6Header.GetDestination(), child)) |
| { |
| aMessage.SetChildMask(childTable.GetChildIndex(child)); |
| mSourceMatchController.IncrementMessageCount(child); |
| } |
| } |
| } |
| } |
| } |
| else if ((neighbor = netif.GetMle().GetNeighbor(ip6Header.GetDestination())) != NULL && |
| !neighbor->IsRxOnWhenIdle() && !aMessage.GetDirectTransmission()) |
| { |
| // destined for a sleepy child |
| Child &child = *static_cast<Child *>(neighbor); |
| aMessage.SetChildMask(childTable.GetChildIndex(child)); |
| mSourceMatchController.IncrementMessageCount(child); |
| } |
| else |
| { |
| // schedule direct transmission |
| aMessage.SetDirectTransmission(); |
| } |
| |
| break; |
| } |
| |
| case Message::kTypeSupervision: |
| { |
| Child *child = netif.GetChildSupervisor().GetDestination(aMessage); |
| VerifyOrExit(child != NULL, error = OT_ERROR_DROP); |
| VerifyOrExit(!child->IsRxOnWhenIdle(), error = OT_ERROR_DROP); |
| |
| aMessage.SetChildMask(childTable.GetChildIndex(*child)); |
| mSourceMatchController.IncrementMessageCount(*child); |
| break; |
| } |
| |
| default: |
| aMessage.SetDirectTransmission(); |
| break; |
| } |
| |
| aMessage.SetOffset(0); |
| aMessage.SetDatagramTag(0); |
| SuccessOrExit(error = mSendQueue.Enqueue(aMessage)); |
| mScheduleTransmissionTask.Post(); |
| |
| exit: |
| return error; |
| } |
| |
| void MeshForwarder::HandleResolved(const Ip6::Address &aEid, otError aError) |
| { |
| Message * cur, *next; |
| Ip6::Address ip6Dst; |
| bool enqueuedMessage = false; |
| |
| for (cur = mResolvingQueue.GetHead(); cur; cur = next) |
| { |
| next = cur->GetNext(); |
| |
| if (cur->GetType() != Message::kTypeIp6) |
| { |
| continue; |
| } |
| |
| cur->Read(Ip6::Header::GetDestinationOffset(), sizeof(ip6Dst), &ip6Dst); |
| |
| if (ip6Dst == aEid) |
| { |
| mResolvingQueue.Dequeue(*cur); |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| mSendQueue.Enqueue(*cur); |
| enqueuedMessage = true; |
| } |
| else |
| { |
| LogMessage(kMessageDrop, *cur, NULL, aError); |
| cur->Free(); |
| } |
| } |
| } |
| |
| if (enqueuedMessage) |
| { |
| mScheduleTransmissionTask.Post(); |
| } |
| } |
| |
| void MeshForwarder::ClearChildIndirectMessages(Child &aChild) |
| { |
| Message *nextMessage; |
| |
| VerifyOrExit(aChild.GetIndirectMessageCount() > 0); |
| |
| for (Message *message = mSendQueue.GetHead(); message; message = nextMessage) |
| { |
| nextMessage = message->GetNext(); |
| |
| message->ClearChildMask(GetNetif().GetMle().GetChildTable().GetChildIndex(aChild)); |
| |
| if (!message->IsChildPending() && !message->GetDirectTransmission()) |
| { |
| if (mSendMessage == message) |
| { |
| mSendMessage = NULL; |
| } |
| |
| mSendQueue.Dequeue(*message); |
| message->Free(); |
| } |
| } |
| |
| aChild.SetIndirectMessage(NULL); |
| mSourceMatchController.ResetMessageCount(aChild); |
| |
| exit: |
| return; |
| } |
| |
| void MeshForwarder::UpdateIndirectMessages(void) |
| { |
| for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptValidOrRestoring); !iter.IsDone(); |
| iter++) |
| { |
| if (iter.GetChild()->GetIndirectMessageCount() == 0) |
| { |
| continue; |
| } |
| |
| ClearChildIndirectMessages(*iter.GetChild()); |
| } |
| } |
| |
| otError MeshForwarder::EvictMessage(uint8_t aPriority) |
| { |
| otError error = OT_ERROR_NOT_FOUND; |
| Message *message; |
| |
| VerifyOrExit((message = mSendQueue.GetTail()) != NULL); |
| |
| if (message->GetPriority() < aPriority) |
| { |
| RemoveMessage(*message); |
| ExitNow(error = OT_ERROR_NONE); |
| } |
| else |
| { |
| while (aPriority <= Message::kPriorityNet) |
| { |
| for (message = mSendQueue.GetHeadForPriority(aPriority); message && (message->GetPriority() == aPriority); |
| message = message->GetNext()) |
| { |
| if (message->IsChildPending()) |
| { |
| RemoveMessage(*message); |
| ExitNow(error = OT_ERROR_NONE); |
| } |
| } |
| |
| aPriority++; |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MeshForwarder::RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t childIndex = GetNetif().GetMle().GetChildTable().GetChildIndex(aChild); |
| |
| VerifyOrExit(aMessage.GetChildMask(childIndex) == true, error = OT_ERROR_NOT_FOUND); |
| |
| aMessage.ClearChildMask(childIndex); |
| mSourceMatchController.DecrementMessageCount(aChild); |
| |
| if (aChild.GetIndirectMessage() == &aMessage) |
| { |
| aChild.SetIndirectMessage(NULL); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| void MeshForwarder::RemoveMessages(Child &aChild, uint8_t aSubType) |
| { |
| ThreadNetif &netif = GetNetif(); |
| Message * nextMessage; |
| |
| for (Message *message = mSendQueue.GetHead(); message; message = nextMessage) |
| { |
| nextMessage = message->GetNext(); |
| |
| if ((aSubType != Message::kSubTypeNone) && (aSubType != message->GetSubType())) |
| { |
| continue; |
| } |
| |
| if (RemoveMessageFromSleepyChild(*message, aChild) != OT_ERROR_NONE) |
| { |
| switch (message->GetType()) |
| { |
| case Message::kTypeIp6: |
| { |
| Ip6::Header ip6header; |
| |
| IgnoreReturnValue(message->Read(0, sizeof(ip6header), &ip6header)); |
| |
| if (&aChild == static_cast<Child *>(netif.GetMle().GetNeighbor(ip6header.GetDestination()))) |
| { |
| message->ClearDirectTransmission(); |
| } |
| |
| break; |
| } |
| |
| case Message::kType6lowpan: |
| { |
| Lowpan::MeshHeader meshHeader; |
| |
| IgnoreReturnValue(meshHeader.Init(*message)); |
| |
| if (&aChild == static_cast<Child *>(netif.GetMle().GetNeighbor(meshHeader.GetDestination()))) |
| { |
| message->ClearDirectTransmission(); |
| } |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| if (!message->IsChildPending() && !message->GetDirectTransmission()) |
| { |
| if (mSendMessage == message) |
| { |
| mSendMessage = NULL; |
| } |
| |
| mSendQueue.Dequeue(*message); |
| message->Free(); |
| } |
| } |
| } |
| |
| void MeshForwarder::RemoveDataResponseMessages(void) |
| { |
| Ip6::Header ip6Header; |
| |
| for (Message *message = mSendQueue.GetHead(); message; message = message->GetNext()) |
| { |
| if (message->GetSubType() != Message::kSubTypeMleDataResponse) |
| { |
| continue; |
| } |
| |
| message->Read(0, sizeof(ip6Header), &ip6Header); |
| |
| if (!(ip6Header.GetDestination().IsMulticast())) |
| { |
| for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++) |
| { |
| IgnoreReturnValue(RemoveMessageFromSleepyChild(*message, *iter.GetChild())); |
| } |
| } |
| |
| if (mSendMessage == message) |
| { |
| mSendMessage = NULL; |
| } |
| |
| mSendQueue.Dequeue(*message); |
| LogMessage(kMessageDrop, *message, NULL, OT_ERROR_NONE); |
| message->Free(); |
| } |
| } |
| |
| otError MeshForwarder::GetIndirectTransmission(void) |
| { |
| otError error = OT_ERROR_NOT_FOUND; |
| ThreadNetif &netif = GetNetif(); |
| |
| UpdateIndirectMessages(); |
| |
| for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); |
| !iter.IsDone(); iter++) |
| { |
| Child &child = *iter.GetChild(); |
| |
| if (!child.IsDataRequestPending()) |
| { |
| continue; |
| } |
| |
| mSendMessage = child.GetIndirectMessage(); |
| mSendMessageMaxCsmaBackoffs = Mac::kMaxCsmaBackoffsIndirect; |
| mSendMessageMaxFrameRetries = Mac::kMaxFrameRetriesIndirect; |
| |
| if (mSendMessage == NULL) |
| { |
| mSendMessage = GetIndirectTransmission(child); |
| } |
| |
| if (mSendMessage != NULL) |
| { |
| PrepareIndirectTransmission(*mSendMessage, child); |
| } |
| else |
| { |
| // A NULL `mSendMessage` triggers an empty frame to be sent to the child. |
| |
| if (child.IsIndirectSourceMatchShort()) |
| { |
| mMacSource.SetShort(netif.GetMac().GetShortAddress()); |
| } |
| else |
| { |
| mMacSource.SetExtended(netif.GetMac().GetExtAddress()); |
| } |
| |
| child.GetMacAddress(mMacDest); |
| } |
| |
| // Remember the current child and move it to next one in the list after the indirect transmission has completed. |
| |
| mIndirectStartingChild = &child; |
| |
| netif.GetMac().SendFrameRequest(); |
| ExitNow(error = OT_ERROR_NONE); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| Message *MeshForwarder::GetIndirectTransmission(Child &aChild) |
| { |
| Message *message = NULL; |
| Message *next; |
| uint8_t childIndex = GetNetif().GetMle().GetChildTable().GetChildIndex(aChild); |
| |
| for (message = mSendQueue.GetHead(); message; message = next) |
| { |
| next = message->GetNext(); |
| |
| if (message->GetChildMask(childIndex)) |
| { |
| // Skip and remove the supervision message if there are other messages queued for the child. |
| |
| if ((message->GetType() == Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1)) |
| { |
| message->ClearChildMask(childIndex); |
| mSourceMatchController.DecrementMessageCount(aChild); |
| mSendQueue.Dequeue(*message); |
| message->Free(); |
| continue; |
| } |
| |
| break; |
| } |
| } |
| |
| aChild.SetIndirectMessage(message); |
| aChild.SetIndirectFragmentOffset(0); |
| aChild.ResetIndirectTxAttempts(); |
| aChild.SetIndirectTxSuccess(true); |
| |
| if (message != NULL) |
| { |
| Mac::Address macAddr; |
| |
| LogMessage(kMessagePrepareIndirect, *message, &aChild.GetMacAddress(macAddr), OT_ERROR_NONE); |
| } |
| |
| return message; |
| } |
| |
| void MeshForwarder::PrepareIndirectTransmission(Message &aMessage, const Child &aChild) |
| { |
| if (aChild.GetIndirectTxAttempts() > 0) |
| { |
| mSendMessageIsARetransmission = true; |
| mSendMessageFrameCounter = aChild.GetIndirectFrameCounter(); |
| mSendMessageKeyId = aChild.GetIndirectKeyId(); |
| mSendMessageDataSequenceNumber = aChild.GetIndirectDataSequenceNumber(); |
| } |
| |
| aMessage.SetOffset(aChild.GetIndirectFragmentOffset()); |
| |
| switch (aMessage.GetType()) |
| { |
| case Message::kTypeIp6: |
| { |
| Ip6::Header ip6Header; |
| |
| aMessage.Read(0, sizeof(ip6Header), &ip6Header); |
| |
| mAddMeshHeader = false; |
| GetMacSourceAddress(ip6Header.GetSource(), mMacSource); |
| |
| if (ip6Header.GetDestination().IsLinkLocal()) |
| { |
| GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest); |
| } |
| else |
| { |
| aChild.GetMacAddress(mMacDest); |
| } |
| |
| break; |
| } |
| |
| case Message::kTypeSupervision: |
| aChild.GetMacAddress(mMacDest); |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| } |
| |
| otError MeshForwarder::SendMesh(Message &aMessage, Mac::Frame &aFrame) |
| { |
| ThreadNetif &netif = GetNetif(); |
| uint16_t fcf; |
| |
| // initialize MAC header |
| fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfFrameVersion2006 | |
| Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfAckRequest | |
| Mac::Frame::kFcfSecurityEnabled; |
| |
| aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32); |
| aFrame.SetDstPanId(netif.GetMac().GetPanId()); |
| aFrame.SetDstAddr(mMacDest.GetShort()); |
| aFrame.SetSrcAddr(mMacSource.GetShort()); |
| |
| // write payload |
| assert(aMessage.GetLength() <= aFrame.GetMaxPayloadLength()); |
| aMessage.Read(0, aMessage.GetLength(), aFrame.GetPayload()); |
| aFrame.SetPayloadLength(static_cast<uint8_t>(aMessage.GetLength())); |
| |
| mMessageNextOffset = aMessage.GetLength(); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| void MeshForwarder::HandleDataRequest(const Mac::Address &aMacSource, const otThreadLinkInfo &aLinkInfo) |
| { |
| ThreadNetif &netif = GetNetif(); |
| Child * child; |
| uint16_t indirectMsgCount; |
| |
| // Security Check: only process secure Data Poll frames. |
| VerifyOrExit(aLinkInfo.mLinkSecurity); |
| |
| VerifyOrExit(netif.GetMle().GetRole() != OT_DEVICE_ROLE_DETACHED); |
| |
| child = netif.GetMle().GetChildTable().FindChild(aMacSource, ChildTable::kInStateValidOrRestoring); |
| VerifyOrExit(child != NULL); |
| |
| child->SetLastHeard(TimerMilli::GetNow()); |
| child->ResetLinkFailures(); |
| indirectMsgCount = child->GetIndirectMessageCount(); |
| |
| if (!mSourceMatchController.IsEnabled() || (indirectMsgCount > 0)) |
| { |
| child->SetDataRequestPending(true); |
| } |
| |
| mScheduleTransmissionTask.Post(); |
| |
| otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d", child->GetRloc16(), indirectMsgCount, aLinkInfo.mRss); |
| |
| exit: |
| return; |
| } |
| |
| void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest) |
| { |
| ThreadNetif &netif = GetNetif(); |
| Child * child; |
| |
| child = netif.GetMle().GetChildTable().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring); |
| VerifyOrExit(child != NULL); |
| |
| child->SetDataRequestPending(false); |
| |
| VerifyOrExit(mSendMessage != NULL); |
| |
| if (mSendMessage == child->GetIndirectMessage()) |
| { |
| // To ensure fairness in handling of data requests from sleepy |
| // children, once a message is completed for indirect transmission to a |
| // child (on both success or failure), the `mIndirectStartingChild` is |
| // updated to the next `Child` entry after the current one. Subsequent |
| // call to `ScheduleTransmissionTask()` will begin the iteration |
| // through the children list from this child. |
| |
| ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild); |
| iter++; |
| mIndirectStartingChild = iter.GetChild(); |
| |
| switch (aError) |
| { |
| case OT_ERROR_NONE: |
| child->ResetIndirectTxAttempts(); |
| break; |
| |
| case OT_ERROR_NO_ACK: |
| child->IncrementIndirectTxAttempts(); |
| // fall through |
| |
| case OT_ERROR_CHANNEL_ACCESS_FAILURE: |
| case OT_ERROR_ABORT: |
| |
| otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d, error:%s", child->GetRloc16(), |
| child->GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts, otThreadErrorToString(aError)); |
| |
| if (child->GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts) |
| { |
| // We save the frame counter, key id, and data sequence number of |
| // current frame so we use the same values for the retransmission |
| // of the frame following the receipt of a data request command (data |
| // poll) from the sleepy child. |
| |
| child->SetIndirectDataSequenceNumber(aFrame.GetSequence()); |
| |
| if (aFrame.GetSecurityEnabled()) |
| { |
| uint32_t frameCounter; |
| uint8_t keyId; |
| |
| aFrame.GetFrameCounter(frameCounter); |
| child->SetIndirectFrameCounter(frameCounter); |
| |
| aFrame.GetKeyId(keyId); |
| child->SetIndirectKeyId(keyId); |
| } |
| |
| ExitNow(); |
| } |
| |
| child->ResetIndirectTxAttempts(); |
| child->SetIndirectTxSuccess(false); |
| |
| #if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE |
| // We set the NextOffset to end of message, since there is no need to |
| // send any remaining fragments in the message to the child, if all tx |
| // attempts of current frame already failed. |
| |
| mMessageNextOffset = mSendMessage->GetLength(); |
| #endif |
| |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| } |
| |
| if (mMessageNextOffset < mSendMessage->GetLength()) |
| { |
| if (mSendMessage == child->GetIndirectMessage()) |
| { |
| child->SetIndirectFragmentOffset(mMessageNextOffset); |
| } |
| } |
| else |
| { |
| otError txError = aError; |
| uint8_t childIndex; |
| |
| if (mSendMessage == child->GetIndirectMessage()) |
| { |
| child->SetIndirectFragmentOffset(0); |
| child->SetIndirectMessage(NULL); |
| child->GetLinkInfo().AddMessageTxStatus(child->GetIndirectTxSuccess()); |
| |
| // Enable short source address matching after the first indirect |
| // message transmission attempt to the child. We intentionally do |
| // not check for successful tx here to address the scenario where |
| // the child does receive "Child ID Response" but parent misses the |
| // 15.4 ack from child. If the "Child ID Response" does not make it |
| // to the child, then the child will need to send a new "Child ID |
| // Request" which will cause the parent to switch to using long |
| // address mode for source address matching. |
| |
| mSourceMatchController.SetSrcMatchAsShort(*child, true); |
| |
| #if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE |
| |
| // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is |
| // disabled, all fragment frames of a larger message are |
| // sent even if the transmission of an earlier fragment fail. |
| // Note that `GetIndirectTxSuccess() tracks the tx success of |
| // the entire message to the child, while `txError = aError` |
| // represents the error status of the last fragment frame |
| // transmission. |
| |
| if (!child->GetIndirectTxSuccess() && (txError == OT_ERROR_NONE)) |
| { |
| txError = OT_ERROR_FAILED; |
| } |
| #endif |
| } |
| |
| childIndex = netif.GetMle().GetChildTable().GetChildIndex(*child); |
| |
| if (mSendMessage->GetChildMask(childIndex)) |
| { |
| mSendMessage->ClearChildMask(childIndex); |
| mSourceMatchController.DecrementMessageCount(*child); |
| } |
| |
| if (!mSendMessage->GetDirectTransmission()) |
| { |
| LogMessage(kMessageTransmit, *mSendMessage, &aMacDest, txError); |
| |
| if (mSendMessage->GetType() == Message::kTypeIp6) |
| { |
| if (mSendMessage->GetTxSuccess()) |
| { |
| mIpCounters.mTxSuccess++; |
| } |
| else |
| { |
| mIpCounters.mTxFailure++; |
| } |
| } |
| } |
| } |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| netif.GetChildSupervisor().UpdateOnSend(*child); |
| } |
| |
| exit: |
| return; |
| } |
| |
| otError MeshForwarder::UpdateMeshRoute(Message &aMessage) |
| { |
| ThreadNetif & netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Lowpan::MeshHeader meshHeader; |
| Neighbor * neighbor; |
| uint16_t nextHop; |
| |
| IgnoreReturnValue(meshHeader.Init(aMessage)); |
| |
| nextHop = netif.GetMle().GetNextHop(meshHeader.GetDestination()); |
| |
| if (nextHop != Mac::kShortAddrInvalid) |
| { |
| neighbor = netif.GetMle().GetNeighbor(nextHop); |
| } |
| else |
| { |
| neighbor = netif.GetMle().GetNeighbor(meshHeader.GetDestination()); |
| } |
| |
| if (neighbor == NULL) |
| { |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| |
| mMacDest.SetShort(neighbor->GetRloc16()); |
| mMacSource.SetShort(netif.GetMac().GetShortAddress()); |
| |
| mAddMeshHeader = true; |
| mMeshDest = meshHeader.GetDestination(); |
| mMeshSource = meshHeader.GetSource(); |
| |
| exit: |
| return error; |
| } |
| |
| otError MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header) |
| { |
| otError error = OT_ERROR_NONE; |
| ThreadNetif &netif = GetNetif(); |
| Neighbor * neighbor; |
| |
| if (netif.GetMle().IsRoutingLocator(ip6Header.GetDestination())) |
| { |
| uint16_t rloc16 = HostSwap16(ip6Header.GetDestination().mFields.m16[7]); |
| VerifyOrExit(netif.GetMle().IsRouterIdValid(netif.GetMle().GetRouterId(rloc16)), error = OT_ERROR_DROP); |
| mMeshDest = rloc16; |
| } |
| else if (netif.GetMle().IsAnycastLocator(ip6Header.GetDestination())) |
| { |
| uint16_t aloc16 = HostSwap16(ip6Header.GetDestination().mFields.m16[7]); |
| |
| if (aloc16 == Mle::kAloc16Leader) |
| { |
| mMeshDest = netif.GetMle().GetRloc16(netif.GetMle().GetLeaderId()); |
| } |
| else if ((aloc16 >= Mle::kAloc16CommissionerStart) && (aloc16 <= Mle::kAloc16CommissionerEnd)) |
| { |
| SuccessOrExit(error = MeshCoP::GetBorderAgentRloc(netif, mMeshDest)); |
| } |
| |
| #if OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT |
| else if (aloc16 <= Mle::kAloc16DhcpAgentEnd) |
| { |
| uint16_t agentRloc16; |
| uint8_t routerId; |
| VerifyOrExit((netif.GetNetworkDataLeader().GetRlocByContextId( |
| static_cast<uint8_t>(aloc16 & Mle::kAloc16DhcpAgentMask), agentRloc16) == OT_ERROR_NONE), |
| error = OT_ERROR_DROP); |
| |
| routerId = netif.GetMle().GetRouterId(agentRloc16); |
| |
| // if agent is active router or the child of the device |
| if ((netif.GetMle().IsActiveRouter(agentRloc16)) || |
| (netif.GetMle().GetRloc16(routerId) == netif.GetMle().GetRloc16())) |
| { |
| mMeshDest = agentRloc16; |
| } |
| else |
| { |
| // use the parent of the ED Agent as Dest |
| mMeshDest = netif.GetMle().GetRloc16(routerId); |
| } |
| } |
| |
| #endif // OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT |
| #if OPENTHREAD_ENABLE_SERVICE |
| else if ((aloc16 >= Mle::kAloc16ServiceStart) && (aloc16 <= Mle::kAloc16ServiceEnd)) |
| { |
| SuccessOrExit(error = GetDestinationRlocByServiceAloc(aloc16, mMeshDest)); |
| } |
| |
| #endif |
| else |
| { |
| // TODO: support for Neighbor Discovery Agent ALOC |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| } |
| else if ((neighbor = netif.GetMle().GetNeighbor(ip6Header.GetDestination())) != NULL) |
| { |
| mMeshDest = neighbor->GetRloc16(); |
| } |
| else if (netif.GetNetworkDataLeader().IsOnMesh(ip6Header.GetDestination())) |
| { |
| SuccessOrExit(error = netif.GetAddressResolver().Resolve(ip6Header.GetDestination(), mMeshDest)); |
| } |
| else |
| { |
| netif.GetNetworkDataLeader().RouteLookup(ip6Header.GetSource(), ip6Header.GetDestination(), NULL, &mMeshDest); |
| } |
| |
| VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = OT_ERROR_DROP); |
| |
| mMeshSource = netif.GetMac().GetShortAddress(); |
| |
| SuccessOrExit(error = netif.GetMle().CheckReachability(mMeshSource, mMeshDest, ip6Header)); |
| mMacDest.SetShort(netif.GetMle().GetNextHop(mMeshDest)); |
| |
| if (mMacDest.GetShort() != mMeshDest) |
| { |
| // destination is not neighbor |
| mMacSource.SetShort(mMeshSource); |
| mAddMeshHeader = true; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError MeshForwarder::GetIp6Header(const uint8_t * aFrame, |
| uint8_t aFrameLength, |
| const Mac::Address &aMacSource, |
| const Mac::Address &aMacDest, |
| Ip6::Header & aIp6Header) |
| { |
| uint8_t headerLength; |
| bool nextHeaderCompressed; |
| |
| return DecompressIp6Header(aFrame, aFrameLength, aMacSource, aMacDest, aIp6Header, headerLength, |
| nextHeaderCompressed); |
| } |
| |
| otError MeshForwarder::CheckReachability(uint8_t * aFrame, |
| uint8_t aFrameLength, |
| const Mac::Address &aMeshSource, |
| const Mac::Address &aMeshDest) |
| { |
| ThreadNetif &netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Ip6::Header ip6Header; |
| |
| SuccessOrExit(error = GetIp6Header(aFrame, aFrameLength, aMeshSource, aMeshDest, ip6Header)); |
| error = netif.GetMle().CheckReachability(aMeshSource.GetShort(), aMeshDest.GetShort(), ip6Header); |
| |
| exit: |
| // the message may not contain an IPv6 header |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else if (error != OT_ERROR_NONE) |
| { |
| error = OT_ERROR_DROP; |
| } |
| |
| return error; |
| } |
| |
| void MeshForwarder::HandleMesh(uint8_t * aFrame, |
| uint8_t aFrameLength, |
| const Mac::Address & aMacSource, |
| const otThreadLinkInfo &aLinkInfo) |
| { |
| ThreadNetif & netif = GetNetif(); |
| otError error = OT_ERROR_NONE; |
| Message * message = NULL; |
| Mac::Address meshDest; |
| Mac::Address meshSource; |
| Lowpan::MeshHeader meshHeader; |
| |
| // Check the mesh header |
| VerifyOrExit(meshHeader.Init(aFrame, aFrameLength) == OT_ERROR_NONE, error = OT_ERROR_DROP); |
| |
| // Security Check: only process Mesh Header frames that had security enabled. |
| VerifyOrExit(aLinkInfo.mLinkSecurity && meshHeader.IsValid(), error = OT_ERROR_SECURITY); |
| |
| meshSource.SetShort(meshHeader.GetSource()); |
| meshDest.SetShort(meshHeader.GetDestination()); |
| |
| UpdateRoutes(aFrame, aFrameLength, meshSource, meshDest); |
| |
| if (meshDest.GetShort() == netif.GetMac().GetShortAddress() || netif.GetMle().IsMinimalChild(meshDest.GetShort())) |
| { |
| aFrame += meshHeader.GetHeaderLength(); |
| aFrameLength -= meshHeader.GetHeaderLength(); |
| |
| if (reinterpret_cast<Lowpan::FragmentHeader *>(aFrame)->IsFragmentHeader()) |
| { |
| HandleFragment(aFrame, aFrameLength, meshSource, meshDest, aLinkInfo); |
| } |
| else if (Lowpan::Lowpan::IsLowpanHc(aFrame)) |
| { |
| HandleLowpanHC(aFrame, aFrameLength, meshSource, meshDest, aLinkInfo); |
| } |
| else |
| { |
| ExitNow(error = OT_ERROR_PARSE); |
| } |
| } |
| else if (meshHeader.GetHopsLeft() > 0) |
| { |
| uint8_t priority = kDefaultMsgPriority; |
| |
| netif.GetMle().ResolveRoutingLoops(aMacSource.GetShort(), meshDest.GetShort()); |
| |
| SuccessOrExit(error = CheckReachability(aFrame, aFrameLength, meshSource, meshDest)); |
| |
| meshHeader.SetHopsLeft(meshHeader.GetHopsLeft() - 1); |
| meshHeader.AppendTo(aFrame); |
| |
| GetForwardFramePriority(aFrame, aFrameLength, meshDest, meshSource, priority); |
| VerifyOrExit((message = GetInstance().GetMessagePool().New(Message::kType6lowpan, priority)) != NULL, |
| error = OT_ERROR_NO_BUFS); |
| SuccessOrExit(error = message->SetLength(aFrameLength)); |
| message->Write(0, aFrameLength, aFrame); |
| message->SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity); |
| message->SetPanId(aLinkInfo.mPanId); |
| message->AddRss(aLinkInfo.mRss); |
| |
| LogMessage(kMessageReceive, *message, &aMacSource, OT_ERROR_NONE); |
| |
| SendMessage(*message); |
| } |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogInfoMac("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", otThreadErrorToString(error), |
| aFrameLength, aMacSource.ToString().AsCString(), aLinkInfo.mLinkSecurity ? "yes" : "no"); |
| |
| if (message != NULL) |
| { |
| message->Free(); |
| } |
| } |
| } |
| |
| void MeshForwarder::UpdateRoutes(uint8_t * aFrame, |
| uint8_t aFrameLength, |
| const Mac::Address &aMeshSource, |
| const Mac::Address &aMeshDest) |
| { |
| ThreadNetif &netif = GetNetif(); |
| Ip6::Header ip6Header; |
| Neighbor * neighbor; |
| |
| VerifyOrExit(!aMeshDest.IsBroadcast() && aMeshSource.IsShort()); |
| SuccessOrExit(GetIp6Header(aFrame, aFrameLength, aMeshSource, aMeshDest, ip6Header)); |
| |
| netif.GetAddressResolver().UpdateCacheEntry(ip6Header.GetSource(), aMeshSource.GetShort()); |
| |
| neighbor = netif.GetMle().GetNeighbor(ip6Header.GetSource()); |
| VerifyOrExit(neighbor != NULL && !neighbor->IsFullThreadDevice()); |
| |
| if (Mle::Mle::GetRouterId(aMeshSource.GetShort()) != Mle::Mle::GetRouterId(GetNetif().GetMac().GetShortAddress())) |
| { |
| netif.GetMle().RemoveNeighbor(*neighbor); |
| } |
| |
| exit: |
| return; |
| } |
| |
| bool MeshForwarder::UpdateFragmentLifetime(void) |
| { |
| bool shouldRun = false; |
| |
| for (size_t i = 0; i < OT_ARRAY_LENGTH(mFragmentEntries); i++) |
| { |
| if (mFragmentEntries[i].GetLifetime() != 0) |
| { |
| mFragmentEntries[i].DecrementLifetime(); |
| |
| if (mFragmentEntries[i].GetLifetime() != 0) |
| { |
| shouldRun = true; |
| } |
| } |
| } |
| |
| return shouldRun; |
| } |
| |
| void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, |
| uint8_t aFragmentLength, |
| uint16_t aSrcRloc16, |
| uint8_t aPriority) |
| { |
| FragmentPriorityEntry *entry; |
| |
| if (aFragmentHeader.GetDatagramOffset() == 0) |
| { |
| VerifyOrExit((entry = GetUnusedFragementPriorityEntry()) != NULL); |
| |
| entry->SetDatagramTag(aFragmentHeader.GetDatagramTag()); |
| entry->SetSrcRloc16(aSrcRloc16); |
| entry->SetPriority(aPriority); |
| entry->SetLifetime(kReassemblyTimeout); |
| |
| if (!mUpdateTimer.IsRunning()) |
| { |
| mUpdateTimer.Start(kStateUpdatePeriod); |
| } |
| } |
| else |
| { |
| VerifyOrExit((entry = FindFragmentPriorityEntry(aFragmentHeader.GetDatagramTag(), aSrcRloc16)) != NULL); |
| |
| entry->SetLifetime(kReassemblyTimeout); |
| |
| if (aFragmentHeader.GetDatagramOffset() + aFragmentLength >= aFragmentHeader.GetDatagramSize()) |
| { |
| entry->SetLifetime(0); |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| FragmentPriorityEntry *MeshForwarder::FindFragmentPriorityEntry(uint16_t aTag, uint16_t aSrcRloc16) |
| { |
| size_t i; |
| |
| for (i = 0; i < OT_ARRAY_LENGTH(mFragmentEntries); i++) |
| { |
| if ((mFragmentEntries[i].GetLifetime() != 0) && (mFragmentEntries[i].GetDatagramTag() == aTag) && |
| (mFragmentEntries[i].GetSrcRloc16() == aSrcRloc16)) |
| { |
| break; |
| } |
| } |
| |
| return (i >= OT_ARRAY_LENGTH(mFragmentEntries)) ? NULL : &mFragmentEntries[i]; |
| } |
| |
| FragmentPriorityEntry *MeshForwarder::GetUnusedFragementPriorityEntry(void) |
| { |
| size_t i; |
| |
| for (i = 0; i < OT_ARRAY_LENGTH(mFragmentEntries); i++) |
| { |
| if (mFragmentEntries[i].GetLifetime() == 0) |
| { |
| break; |
| } |
| } |
| |
| return (i >= OT_ARRAY_LENGTH(mFragmentEntries)) ? NULL : &mFragmentEntries[i]; |
| } |
| |
| otError MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, |
| uint16_t aSrcRloc16, |
| uint8_t & aPriority) |
| { |
| otError error = OT_ERROR_NONE; |
| FragmentPriorityEntry *entry; |
| |
| VerifyOrExit((entry = FindFragmentPriorityEntry(aFragmentHeader.GetDatagramTag(), aSrcRloc16)) != NULL, |
| error = OT_ERROR_NOT_FOUND); |
| aPriority = entry->GetPriority(); |
| |
| exit: |
| return error; |
| } |
| |
| otError MeshForwarder::GetForwardFramePriority(const uint8_t * aFrame, |
| uint8_t aFrameLength, |
| const Mac::Address &aMacDest, |
| const Mac::Address &aMacSource, |
| uint8_t & aPriority) |
| { |
| otError error = OT_ERROR_NONE; |
| bool isFragment = false; |
| Lowpan::MeshHeader meshHeader; |
| Lowpan::FragmentHeader fragmentHeader; |
| |
| SuccessOrExit(error = GetMeshHeader(aFrame, aFrameLength, meshHeader)); |
| aFrame += meshHeader.GetHeaderLength(); |
| aFrameLength -= meshHeader.GetHeaderLength(); |
| |
| if (GetFragmentHeader(aFrame, aFrameLength, fragmentHeader) == OT_ERROR_NONE) |
| { |
| isFragment = true; |
| aFrame += fragmentHeader.GetHeaderLength(); |
| aFrameLength -= fragmentHeader.GetHeaderLength(); |
| |
| if (fragmentHeader.GetDatagramOffset() > 0) |
| { |
| // Get priority from the pre-buffered info |
| ExitNow(error = GetFragmentPriority(fragmentHeader, meshHeader.GetSource(), aPriority)); |
| } |
| } |
| |
| // Get priority from IPv6 header or UDP destination port directly |
| error = GetFramePriority(aFrame, aFrameLength, aMacSource, aMacDest, aPriority); |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| otLogNoteMac("Failed to get forwarded frame priority, error:%s, len:%d, dst:%s, src:%s", |
| otThreadErrorToString(error), aFrameLength, aMacDest.ToString().AsCString(), |
| aMacSource.ToString().AsCString()); |
| } |
| else if (isFragment) |
| { |
| UpdateFragmentPriority(fragmentHeader, aFrameLength, meshHeader.GetSource(), aPriority); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_SERVICE |
| otError MeshForwarder::GetDestinationRlocByServiceAloc(uint16_t aServiceAloc, uint16_t &aMeshDest) |
| { |
| otError error = OT_ERROR_NONE; |
| ThreadNetif & netif = GetNetif(); |
| uint8_t serviceId = netif.GetMle().GetServiceIdFromAloc(aServiceAloc); |
| NetworkData::ServiceTlv *serviceTlv = netif.GetNetworkDataLeader().FindServiceById(serviceId); |
| |
| if (serviceTlv != NULL) |
| { |
| NetworkData::NetworkDataTlv *cur = serviceTlv->GetSubTlvs(); |
| NetworkData::NetworkDataTlv *end = serviceTlv->GetNext(); |
| NetworkData::ServerTlv * server; |
| uint8_t bestCost = Mle::kMaxRouteCost; |
| uint8_t curCost = 0x00; |
| uint16_t bestDest = Mac::kShortAddrInvalid; |
| |
| while (cur < end) |
| { |
| switch (cur->GetType()) |
| { |
| case NetworkData::NetworkDataTlv::kTypeServer: |
| server = static_cast<NetworkData::ServerTlv *>(cur); |
| curCost = netif.GetMle().GetCost(server->GetServer16()); |
| |
| if ((bestDest == Mac::kShortAddrInvalid) || (curCost < bestCost)) |
| { |
| bestDest = server->GetServer16(); |
| bestCost = curCost; |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| cur = cur->GetNext(); |
| } |
| |
| if (bestDest != Mac::kShortAddrInvalid) |
| { |
| aMeshDest = bestDest; |
| } |
| else |
| { |
| // ServiceTLV without ServerTLV? Can't forward packet anywhere. |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| } |
| else |
| { |
| // Unknown service, can't forward |
| ExitNow(error = OT_ERROR_DROP); |
| } |
| |
| exit: |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_SERVICE |
| |
| #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1) |
| |
| otError MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, |
| const Message & aMessage, |
| const Mac::Address *aMacAddress, |
| otError aError, |
| uint16_t & aOffset, |
| Mac::Address & aMeshSource, |
| Mac::Address & aMeshDest, |
| otLogLevel aLogLevel) |
| { |
| otError error = OT_ERROR_FAILED; |
| bool hasFragmentHeader = false; |
| bool shouldLogRss; |
| Lowpan::MeshHeader meshHeader; |
| Lowpan::FragmentHeader fragmentHeader; |
| |
| SuccessOrExit(meshHeader.Init(aMessage)); |
| VerifyOrExit(meshHeader.IsMeshHeader()); |
| |
| aMeshSource.SetShort(meshHeader.GetSource()); |
| aMeshDest.SetShort(meshHeader.GetDestination()); |
| |
| aOffset = meshHeader.GetHeaderLength(); |
| |
| if (fragmentHeader.Init(aMessage, aOffset) == OT_ERROR_NONE) |
| { |
| hasFragmentHeader = true; |
| aOffset += fragmentHeader.GetHeaderLength(); |
| } |
| |
| shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop); |
| |
| otLogMac( |
| aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s", |
| MessageActionToString(aAction, aError), aMessage.GetLength(), |
| (aMacAddress == NULL) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"), |
| (aMacAddress == NULL) ? "" : aMacAddress->ToString().AsCString(), aMeshSource.ToString().AsCString(), |
| aMeshDest.ToString().AsCString(), meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), |
| hasFragmentHeader ? "yes" : "no", aMessage.IsLinkSecurityEnabled() ? "yes" : "no", |
| (aError == OT_ERROR_NONE) ? "" : ", error:", (aError == OT_ERROR_NONE) ? "" : otThreadErrorToString(aError), |
| shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : ""); |
| |
| if (hasFragmentHeader) |
| { |
| otLogMac(aLogLevel, "\tFrag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(), |
| fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize()); |
| |
| VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0); |
| } |
| |
| error = OT_ERROR_NONE; |
| |
| exit: |
| return error; |
| } |
| |
| otError MeshForwarder::DecompressIp6UdpTcpHeader(const Message & aMessage, |
| uint16_t aOffset, |
| const Mac::Address &aMeshSource, |
| const Mac::Address &aMeshDest, |
| Ip6::Header & aIp6Header, |
| uint16_t & aChecksum, |
| uint16_t & aSourcePort, |
| uint16_t & aDestPort) |
| { |
| otError error = OT_ERROR_PARSE; |
| Lowpan::Lowpan &lowpan = GetNetif().GetLowpan(); |
| int headerLength; |
| bool nextHeaderCompressed; |
| uint8_t frameBuffer[sizeof(Ip6::Header)]; |
| uint16_t frameLength; |
| union |
| { |
| Ip6::UdpHeader udp; |
| Ip6::TcpHeader tcp; |
| } header; |
| |
| aChecksum = 0; |
| aSourcePort = 0; |
| aDestPort = 0; |
| |
| // Read and decompress the IPv6 header |
| |
| frameLength = aMessage.Read(aOffset, sizeof(frameBuffer), frameBuffer); |
| |
| headerLength = |
| lowpan.DecompressBaseHeader(aIp6Header, nextHeaderCompressed, aMeshSource, aMeshDest, frameBuffer, frameLength); |
| VerifyOrExit(headerLength >= 0); |
| |
| aOffset += headerLength; |
| |
| // Read and decompress UDP or TCP header |
| |
| switch (aIp6Header.GetNextHeader()) |
| { |
| case Ip6::kProtoUdp: |
| if (nextHeaderCompressed) |
| { |
| frameLength = aMessage.Read(aOffset, sizeof(Ip6::UdpHeader), frameBuffer); |
| headerLength = lowpan.DecompressUdpHeader(header.udp, frameBuffer, frameLength); |
| VerifyOrExit(headerLength >= 0); |
| } |
| else |
| { |
| VerifyOrExit(sizeof(Ip6::UdpHeader) == aMessage.Read(aOffset, sizeof(Ip6::UdpHeader), &header.udp)); |
| } |
| |
| aChecksum = header.udp.GetChecksum(); |
| aSourcePort = header.udp.GetSourcePort(); |
| aDestPort = header.udp.GetDestinationPort(); |
| break; |
| |
| case Ip6::kProtoTcp: |
| VerifyOrExit(sizeof(Ip6::TcpHeader) == aMessage.Read(aOffset, sizeof(Ip6::TcpHeader), &header.tcp)); |
| aChecksum = header.tcp.GetChecksum(); |
| aSourcePort = header.tcp.GetSourcePort(); |
| aDestPort = header.tcp.GetDestinationPort(); |
| break; |
| |
| default: |
| break; |
| } |
| |
| error = OT_ERROR_NONE; |
| |
| exit: |
| return error; |
| } |
| |
| void MeshForwarder::LogMeshIpHeader(const Message & aMessage, |
| uint16_t aOffset, |
| const Mac::Address &aMeshSource, |
| const Mac::Address &aMeshDest, |
| otLogLevel aLogLevel) |
| { |
| uint16_t checksum; |
| uint16_t sourcePort; |
| uint16_t destPort; |
| Ip6::Header ip6Header; |
| |
| SuccessOrExit(DecompressIp6UdpTcpHeader(aMessage, aOffset, aMeshSource, aMeshDest, ip6Header, checksum, sourcePort, |
| destPort)); |
| |
| otLogMac(aLogLevel, "\tIPv6 %s msg, chksum:%04x, prio:%s", Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()), |
| checksum, MessagePriorityToString(aMessage)); |
| |
| LogIp6SourceDestAddresses(ip6Header, sourcePort, destPort, aLogLevel); |
| |
| exit: |
| return; |
| } |
| |
| void MeshForwarder::LogMeshMessage(MessageAction aAction, |
| const Message & aMessage, |
| const Mac::Address *aMacAddress, |
| otError aError, |
| otLogLevel aLogLevel) |
| { |
| uint16_t offset; |
| Mac::Address meshSource; |
| Mac::Address meshDest; |
| |
| SuccessOrExit( |
| LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshSource, meshDest, aLogLevel)); |
| |
| // When log action is `kMessageTransmit` we do not include |
| // the IPv6 header info in the logs, as the same info is |
| // logged when the same Mesh Header message was received |
| // and info about it was logged. |
| |
| VerifyOrExit(aAction != kMessageTransmit); |
| |
| LogMeshIpHeader(aMessage, offset, meshSource, meshDest, aLogLevel); |
| |
| exit: |
| return; |
| } |
| |
| #endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1) |
| |
| } // namespace ot |
| |
| #endif // OPENTHREAD_FTD |