blob: 1baa93b016cdb49835c1b1179b0ee5ef9ed6e88b [file] [log] [blame]
/*
* Copyright (c) 2016, 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 mesh forwarding of IPv6/6LoWPAN messages.
*/
#define WPP_NAME "mesh_forwarder.tmh"
#include "mesh_forwarder.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/logging.hpp"
#include "common/message.hpp"
#include "common/owner-locator.hpp"
#include "common/random.hpp"
#include "net/ip6.hpp"
#include "net/ip6_filter.hpp"
#include "net/netif.hpp"
#include "net/tcp.hpp"
#include "net/udp6.hpp"
#include "thread/mle.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
using ot::Encoding::BigEndian::HostSwap16;
namespace ot {
MeshForwarder::MeshForwarder(Instance &aInstance)
: InstanceLocator(aInstance)
, mMacReceiver(&MeshForwarder::HandleReceivedFrame, &MeshForwarder::HandleDataPollTimeout, this)
, mMacSender(&MeshForwarder::HandleFrameRequest, &MeshForwarder::HandleSentFrame, this)
, mDiscoverTimer(aInstance, &MeshForwarder::HandleDiscoverTimer, this)
, mReassemblyTimer(aInstance, &MeshForwarder::HandleReassemblyTimer, this)
, mMessageNextOffset(0)
, mSendMessage(NULL)
, mSendMessageIsARetransmission(false)
, mSendMessageMaxCsmaBackoffs(Mac::kMaxCsmaBackoffsDirect)
, mSendMessageMaxFrameRetries(Mac::kMaxFrameRetriesDirect)
, mMeshSource()
, mMeshDest()
, mAddMeshHeader(false)
, mSendBusy(false)
, mScheduleTransmissionTask(aInstance, ScheduleTransmissionTask, this)
, mEnabled(false)
, mScanChannels(0)
, mScanChannel(0)
, mMacRadioAcquisitionId(0)
, mRestorePanId(Mac::kPanIdBroadcast)
, mScanning(false)
#if OPENTHREAD_FTD
, mSourceMatchController(aInstance)
, mSendMessageFrameCounter(0)
, mSendMessageKeyId(0)
, mSendMessageDataSequenceNumber(0)
, mIndirectStartingChild(NULL)
#endif
, mDataPollManager(aInstance)
{
mFragTag = Random::GetUint16();
GetNetif().GetMac().RegisterReceiver(mMacReceiver);
mIpCounters.mTxSuccess = 0;
mIpCounters.mRxSuccess = 0;
mIpCounters.mTxFailure = 0;
mIpCounters.mRxFailure = 0;
}
otError MeshForwarder::Start(void)
{
otError error = OT_ERROR_NONE;
if (mEnabled == false)
{
GetNetif().GetMac().SetRxOnWhenIdle(true);
mEnabled = true;
}
return error;
}
otError MeshForwarder::Stop(void)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
Message * message;
VerifyOrExit(mEnabled == true);
mDataPollManager.StopPolling();
mReassemblyTimer.Stop();
if (mScanning)
{
HandleDiscoverComplete();
}
while ((message = mSendQueue.GetHead()) != NULL)
{
mSendQueue.Dequeue(*message);
message->Free();
}
while ((message = mReassemblyList.GetHead()) != NULL)
{
mReassemblyList.Dequeue(*message);
message->Free();
}
mEnabled = false;
mSendMessage = NULL;
netif.GetMac().SetRxOnWhenIdle(false);
exit:
return error;
}
void MeshForwarder::RemoveMessage(Message &aMessage)
{
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++)
{
IgnoreReturnValue(RemoveMessageFromSleepyChild(aMessage, *iter.GetChild()));
}
if (mSendMessage == &aMessage)
{
mSendMessage = NULL;
}
mSendQueue.Dequeue(aMessage);
LogMessage(kMessageEvict, aMessage, NULL, OT_ERROR_NO_BUFS);
aMessage.Free();
}
void MeshForwarder::ScheduleTransmissionTask(Tasklet &aTasklet)
{
aTasklet.GetOwner<MeshForwarder>().ScheduleTransmissionTask();
}
void MeshForwarder::ScheduleTransmissionTask(void)
{
VerifyOrExit(mSendBusy == false);
mSendMessageIsARetransmission = false;
if (GetIndirectTransmission() == OT_ERROR_NONE)
{
ExitNow();
}
if ((mSendMessage = GetDirectTransmission()) != NULL)
{
if (mSendMessage->GetOffset() == 0)
{
mSendMessage->SetTxSuccess(true);
}
mSendMessageMaxCsmaBackoffs = Mac::kMaxCsmaBackoffsDirect;
mSendMessageMaxFrameRetries = Mac::kMaxFrameRetriesDirect;
GetNetif().GetMac().SendFrameRequest(mMacSender);
ExitNow();
}
exit:
return;
}
otError MeshForwarder::PrepareDiscoverRequest(void)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
VerifyOrExit(!mScanning);
mScanChannel = OT_RADIO_CHANNEL_MIN;
mScanChannels >>= OT_RADIO_CHANNEL_MIN;
mRestorePanId = netif.GetMac().GetPanId();
SuccessOrExit(error = netif.GetMac().AcquireRadioChannel(&mMacRadioAcquisitionId));
mScanning = true;
while ((mScanChannels & 1) == 0)
{
mScanChannels >>= 1;
mScanChannel++;
if (mScanChannel > OT_RADIO_CHANNEL_MAX)
{
HandleDiscoverComplete();
ExitNow(error = OT_ERROR_DROP);
}
}
exit:
return error;
}
Message *MeshForwarder::GetDirectTransmission(void)
{
Message *curMessage, *nextMessage;
otError error = OT_ERROR_NONE;
for (curMessage = mSendQueue.GetHead(); curMessage; curMessage = nextMessage)
{
nextMessage = curMessage->GetNext();
if (curMessage->GetDirectTransmission() == false)
{
continue;
}
switch (curMessage->GetType())
{
case Message::kTypeIp6:
error = UpdateIp6Route(*curMessage);
if (curMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
error = PrepareDiscoverRequest();
}
break;
case Message::kTypeMacDataPoll:
error = PrepareDataPoll();
break;
#if OPENTHREAD_FTD
case Message::kType6lowpan:
error = UpdateMeshRoute(*curMessage);
break;
#endif
default:
error = OT_ERROR_DROP;
break;
}
switch (error)
{
case OT_ERROR_NONE:
ExitNow();
#if OPENTHREAD_FTD
case OT_ERROR_ADDRESS_QUERY:
mSendQueue.Dequeue(*curMessage);
mResolvingQueue.Enqueue(*curMessage);
continue;
#endif
case OT_ERROR_DROP:
case OT_ERROR_NO_BUFS:
mSendQueue.Dequeue(*curMessage);
LogMessage(kMessageDrop, *curMessage, NULL, error);
curMessage->Free();
continue;
default:
assert(false);
break;
}
}
exit:
return curMessage;
}
otError MeshForwarder::PrepareDataPoll(void)
{
otError error = OT_ERROR_NONE;
ThreadNetif &netif = GetNetif();
Neighbor * parent = netif.GetMle().GetParentCandidate();
uint16_t shortAddress;
VerifyOrExit((parent != NULL) && parent->IsStateValidOrRestoring(), error = OT_ERROR_DROP);
shortAddress = netif.GetMac().GetShortAddress();
if ((shortAddress == Mac::kShortAddrInvalid) || (parent != netif.GetMle().GetParent()))
{
mMacSource.SetExtended(netif.GetMac().GetExtAddress());
mMacDest.SetExtended(parent->GetExtAddress());
}
else
{
mMacSource.SetShort(shortAddress);
mMacDest.SetShort(parent->GetRloc16());
}
exit:
return error;
}
otError MeshForwarder::UpdateIp6Route(Message &aMessage)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
Ip6::Header ip6Header;
mAddMeshHeader = false;
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
VerifyOrExit(!ip6Header.GetSource().IsMulticast(), error = OT_ERROR_DROP);
// 1. Choose correct MAC Source Address.
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
// 2. Choose correct MAC Destination Address.
if (netif.GetMle().GetRole() == OT_DEVICE_ROLE_DISABLED || netif.GetMle().GetRole() == OT_DEVICE_ROLE_DETACHED)
{
// Allow only for link-local unicasts and multicasts.
if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast())
{
GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest);
}
else
{
error = OT_ERROR_DROP;
}
ExitNow();
}
if (ip6Header.GetDestination().IsMulticast())
{
// With the exception of MLE multicasts, a Thread End Device transmits multicasts,
// as IEEE 802.15.4 unicasts to its parent.
if (netif.GetMle().GetRole() == OT_DEVICE_ROLE_CHILD && !aMessage.IsSubTypeMle())
{
mMacDest.SetShort(netif.GetMle().GetNextHop(Mac::kShortAddrBroadcast));
}
else
{
mMacDest.SetShort(Mac::kShortAddrBroadcast);
}
}
else if (ip6Header.GetDestination().IsLinkLocal())
{
GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest);
}
else if (netif.GetMle().IsMinimalEndDevice())
{
mMacDest.SetShort(netif.GetMle().GetNextHop(Mac::kShortAddrBroadcast));
}
else
{
#if OPENTHREAD_FTD
error = UpdateIp6RouteFtd(ip6Header);
#else
assert(false);
#endif
}
exit:
return error;
}
bool MeshForwarder::GetRxOnWhenIdle(void)
{
return GetNetif().GetMac().GetRxOnWhenIdle();
}
void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle)
{
ThreadNetif &netif = GetNetif();
netif.GetMac().SetRxOnWhenIdle(aRxOnWhenIdle);
if (aRxOnWhenIdle)
{
mDataPollManager.StopPolling();
netif.GetSupervisionListener().Stop();
}
else
{
mDataPollManager.StartPolling();
netif.GetSupervisionListener().Start();
}
}
otError MeshForwarder::GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr)
{
ThreadNetif &netif = GetNetif();
aIp6Addr.ToExtAddress(aMacAddr);
if (aMacAddr.GetExtended() != netif.GetMac().GetExtAddress())
{
aMacAddr.SetShort(netif.GetMac().GetShortAddress());
}
return OT_ERROR_NONE;
}
otError MeshForwarder::GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr)
{
if (aIp6Addr.IsMulticast())
{
aMacAddr.SetShort(Mac::kShortAddrBroadcast);
}
else if (aIp6Addr.mFields.m16[0] == HostSwap16(0xfe80) && aIp6Addr.mFields.m16[1] == HostSwap16(0x0000) &&
aIp6Addr.mFields.m16[2] == HostSwap16(0x0000) && aIp6Addr.mFields.m16[3] == HostSwap16(0x0000) &&
aIp6Addr.mFields.m16[4] == HostSwap16(0x0000) && aIp6Addr.mFields.m16[5] == HostSwap16(0x00ff) &&
aIp6Addr.mFields.m16[6] == HostSwap16(0xfe00))
{
aMacAddr.SetShort(HostSwap16(aIp6Addr.mFields.m16[7]));
}
else if (GetNetif().GetMle().IsRoutingLocator(aIp6Addr))
{
aMacAddr.SetShort(HostSwap16(aIp6Addr.mFields.m16[7]));
}
else
{
aIp6Addr.ToExtAddress(aMacAddr);
}
return OT_ERROR_NONE;
}
otError MeshForwarder::SkipMeshHeader(const uint8_t *&aFrame, uint8_t &aFrameLength)
{
otError error = OT_ERROR_NONE;
Lowpan::MeshHeader meshHeader;
VerifyOrExit(aFrameLength >= 1 && reinterpret_cast<const Lowpan::MeshHeader *>(aFrame)->IsMeshHeader());
SuccessOrExit(error = meshHeader.Init(aFrame, aFrameLength));
aFrame += meshHeader.GetHeaderLength();
aFrameLength -= meshHeader.GetHeaderLength();
exit:
return error;
}
otError MeshForwarder::DecompressIp6Header(const uint8_t * aFrame,
uint8_t aFrameLength,
const Mac::Address &aMacSource,
const Mac::Address &aMacDest,
Ip6::Header & aIp6Header,
uint8_t & aHeaderLength,
bool & aNextHeaderCompressed)
{
Lowpan::Lowpan & lowpan = GetNetif().GetLowpan();
otError error = OT_ERROR_NONE;
const uint8_t * start = aFrame;
Lowpan::FragmentHeader fragmentHeader;
int headerLength;
SuccessOrExit(error = SkipMeshHeader(aFrame, aFrameLength));
if (aFrameLength >= 1 && reinterpret_cast<const Lowpan::FragmentHeader *>(aFrame)->IsFragmentHeader())
{
SuccessOrExit(error = fragmentHeader.Init(aFrame, aFrameLength));
// only the first fragment header is followed by a LOWPAN_IPHC header
VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0, error = OT_ERROR_NOT_FOUND);
aFrame += fragmentHeader.GetHeaderLength();
aFrameLength -= fragmentHeader.GetHeaderLength();
}
VerifyOrExit(aFrameLength >= 1 && Lowpan::Lowpan::IsLowpanHc(aFrame), error = OT_ERROR_NOT_FOUND);
headerLength =
lowpan.DecompressBaseHeader(aIp6Header, aNextHeaderCompressed, aMacSource, aMacDest, aFrame, aFrameLength);
VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE);
aHeaderLength = static_cast<uint8_t>(aFrame - start) + static_cast<uint8_t>(headerLength);
exit:
return error;
}
otError MeshForwarder::HandleFrameRequest(Mac::Sender &aSender, Mac::Frame &aFrame)
{
return aSender.GetOwner<MeshForwarder>().HandleFrameRequest(aFrame);
}
otError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
VerifyOrExit(mEnabled, error = OT_ERROR_ABORT);
mSendBusy = true;
if (mSendMessage == NULL)
{
SendEmptyFrame(aFrame, false);
aFrame.SetIsARetransmission(false);
aFrame.SetMaxCsmaBackoffs(Mac::kMaxCsmaBackoffsDirect);
aFrame.SetMaxFrameRetries(Mac::kMaxFrameRetriesDirect);
ExitNow();
}
switch (mSendMessage->GetType())
{
case Message::kTypeIp6:
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
SuccessOrExit(error = netif.GetMac().SetRadioChannel(mMacRadioAcquisitionId, mScanChannel));
aFrame.SetChannel(mScanChannel);
// In case a specific PAN ID of a Thread Network to be discovered is not known, Discovery
// Request messages MUST have the Destination PAN ID in the IEEE 802.15.4 MAC header set
// to be the Broadcast PAN ID (0xFFFF) and the Source PAN ID set to a randomly generated
// value.
if (mSendMessage->GetPanId() == Mac::kPanIdBroadcast && netif.GetMac().GetPanId() == Mac::kPanIdBroadcast)
{
uint16_t panid;
do
{
panid = Random::GetUint16();
} while (panid == Mac::kPanIdBroadcast);
netif.GetMac().SetPanId(panid);
}
}
error = SendFragment(*mSendMessage, aFrame);
// `SendFragment()` fails with `NotCapable` error if the message is MLE (with
// no link layer security) and also requires fragmentation.
if (error == OT_ERROR_NOT_CAPABLE)
{
// Enable security and try again.
mSendMessage->SetLinkSecurityEnabled(true);
error = SendFragment(*mSendMessage, aFrame);
}
assert(aFrame.GetLength() != 7);
break;
case Message::kTypeMacDataPoll:
error = SendPoll(*mSendMessage, aFrame);
break;
#if OPENTHREAD_FTD
case Message::kType6lowpan:
error = SendMesh(*mSendMessage, aFrame);
break;
case Message::kTypeSupervision:
error = SendEmptyFrame(aFrame, kSupervisionMsgAckRequest);
mMessageNextOffset = mSendMessage->GetLength();
break;
#endif
}
assert(error == OT_ERROR_NONE);
aFrame.SetIsARetransmission(mSendMessageIsARetransmission);
aFrame.SetMaxCsmaBackoffs(mSendMessageMaxCsmaBackoffs);
aFrame.SetMaxFrameRetries(mSendMessageMaxFrameRetries);
#if OPENTHREAD_FTD
{
Mac::Address macDest;
Child * child = NULL;
if (mSendMessageIsARetransmission)
{
// If this is the re-transmission of an indirect frame to a sleepy child, we
// ensure to use the same frame counter, key id, and data sequence number as
// the last attempt.
aFrame.SetSequence(mSendMessageDataSequenceNumber);
if (aFrame.GetSecurityEnabled())
{
aFrame.SetFrameCounter(mSendMessageFrameCounter);
aFrame.SetKeyId(mSendMessageKeyId);
}
}
aFrame.GetDstAddr(macDest);
// Set `FramePending` if there are more queued messages (excluding
// the current one being sent out) for the child (note `> 1` check).
// The case where the current message requires fragmentation is
// already checked and handled in `SendFragment()` method.
child = netif.GetMle().GetChildTable().FindChild(macDest, ChildTable::kInStateValidOrRestoring);
if ((child != NULL) && !child->IsRxOnWhenIdle() && (child->GetIndirectMessageCount() > 1))
{
aFrame.SetFramePending(true);
}
}
#endif
exit:
return error;
}
otError MeshForwarder::SendPoll(Message &aMessage, Mac::Frame &aFrame)
{
ThreadNetif &netif = GetNetif();
uint16_t fcf;
// initialize MAC header
fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfFrameVersion2006;
if (mMacSource.IsShort())
{
fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort;
}
else
{
fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt;
}
fcf |= Mac::Frame::kFcfAckRequest | Mac::Frame::kFcfSecurityEnabled;
aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
aFrame.SetDstPanId(netif.GetMac().GetPanId());
aFrame.SetSrcAddr(mMacSource);
aFrame.SetDstAddr(mMacDest);
aFrame.SetCommandId(Mac::Frame::kMacCmdDataRequest);
mMessageNextOffset = aMessage.GetLength();
return OT_ERROR_NONE;
}
otError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame)
{
ThreadNetif & netif = GetNetif();
Mac::Address meshDest, meshSource;
uint16_t fcf;
Lowpan::FragmentHeader *fragmentHeader;
uint8_t * payload;
uint8_t headerLength;
uint16_t payloadLength;
int hcLength;
uint16_t fragmentLength;
uint16_t dstpan;
uint8_t secCtl = Mac::Frame::kSecNone;
otError error = OT_ERROR_NONE;
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
Mac::HeaderIe ieList[2];
#endif
if (mAddMeshHeader)
{
meshSource.SetShort(mMeshSource);
meshDest.SetShort(mMeshDest);
}
else
{
meshDest = mMacDest;
meshSource = mMacSource;
}
// initialize MAC header
fcf = Mac::Frame::kFcfFrameData;
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
if (aMessage.IsTimeSync())
{
fcf |= Mac::Frame::kFcfFrameVersion2015 | Mac::Frame::kFcfIePresent;
}
else
#endif
{
fcf |= Mac::Frame::kFcfFrameVersion2006;
}
fcf |= (mMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (mMacSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
// all unicast frames request ACK
if (mMacDest.IsExtended() || !mMacDest.IsBroadcast())
{
fcf |= Mac::Frame::kFcfAckRequest;
}
if (aMessage.IsLinkSecurityEnabled())
{
fcf |= Mac::Frame::kFcfSecurityEnabled;
switch (aMessage.GetSubType())
{
case Message::kSubTypeJoinerEntrust:
secCtl = static_cast<uint8_t>(Mac::Frame::kKeyIdMode0);
break;
case Message::kSubTypeMleAnnounce:
secCtl = static_cast<uint8_t>(Mac::Frame::kKeyIdMode2);
break;
default:
secCtl = static_cast<uint8_t>(Mac::Frame::kKeyIdMode1);
break;
}
secCtl |= Mac::Frame::kSecEncMic32;
}
dstpan = netif.GetMac().GetPanId();
switch (aMessage.GetSubType())
{
case Message::kSubTypeMleAnnounce:
aFrame.SetChannel(aMessage.GetChannel());
dstpan = Mac::kPanIdBroadcast;
break;
case Message::kSubTypeMleDiscoverRequest:
case Message::kSubTypeMleDiscoverResponse:
dstpan = aMessage.GetPanId();
break;
default:
break;
}
if (dstpan == netif.GetMac().GetPanId())
{
#if OPENTHREAD_CONFIG_HEADER_IE_SUPPORT
// Handle a special case in IEEE 802.15.4-2015, when Pan ID Compression is 0, but Src Pan ID is not present:
// Dest Address: Extended
// Src Address: Extended
// Dest Pan ID: Present
// Src Pan ID: Not Present
// Pan ID Compression: 0
if ((fcf & Mac::Frame::kFcfFrameVersionMask) != Mac::Frame::kFcfFrameVersion2015 ||
(fcf & Mac::Frame::kFcfDstAddrMask) != Mac::Frame::kFcfDstAddrExt ||
(fcf & Mac::Frame::kFcfSrcAddrMask) != Mac::Frame::kFcfSrcAddrExt)
#endif
{
fcf |= Mac::Frame::kFcfPanidCompression;
}
}
aFrame.InitMacHeader(fcf, secCtl);
aFrame.SetDstPanId(dstpan);
aFrame.SetSrcPanId(netif.GetMac().GetPanId());
aFrame.SetDstAddr(mMacDest);
aFrame.SetSrcAddr(mMacSource);
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
if (aMessage.IsTimeSync())
{
Mac::TimeIe *ie;
uint8_t * cur = NULL;
ieList[0].Init();
ieList[0].SetId(Mac::Frame::kHeaderIeVendor);
ieList[0].SetLength(sizeof(Mac::TimeIe));
ieList[1].Init();
ieList[1].SetId(Mac::Frame::kHeaderIeTermination2);
ieList[1].SetLength(0);
aFrame.AppendHeaderIe(ieList, 2);
cur = aFrame.GetHeaderIe(Mac::Frame::kHeaderIeVendor);
ie = reinterpret_cast<Mac::TimeIe *>(cur + sizeof(Mac::HeaderIe));
ie->Init();
}
#endif
payload = aFrame.GetPayload();
headerLength = 0;
#if OPENTHREAD_FTD
// initialize Mesh header
if (mAddMeshHeader)
{
Lowpan::MeshHeader meshHeader;
uint8_t hopsLeft;
if (netif.GetMle().GetRole() == OT_DEVICE_ROLE_CHILD)
{
// REED sets hopsLeft to max (16) + 1. It does not know the route cost.
hopsLeft = Mle::kMaxRouteCost + 1;
}
else
{
// Calculate the number of predicted hops.
hopsLeft = netif.GetMle().GetRouteCost(mMeshDest);
if (hopsLeft != Mle::kMaxRouteCost)
{
hopsLeft +=
netif.GetMle().GetLinkCost(netif.GetMle().GetRouterId(netif.GetMle().GetNextHop(mMeshDest)));
}
else
{
// In case there is no route to the destination router (only link).
hopsLeft = netif.GetMle().GetLinkCost(netif.GetMle().GetRouterId(mMeshDest));
}
}
// The hopsLft field MUST be incremented by one if the destination RLOC16
// is not that of an active Router.
if (!netif.GetMle().IsActiveRouter(mMeshDest))
{
hopsLeft += 1;
}
meshHeader.Init();
meshHeader.SetHopsLeft(hopsLeft + Lowpan::MeshHeader::kAdditionalHopsLeft);
meshHeader.SetSource(mMeshSource);
meshHeader.SetDestination(mMeshDest);
meshHeader.AppendTo(payload);
payload += meshHeader.GetHeaderLength();
headerLength += meshHeader.GetHeaderLength();
}
#endif
// copy IPv6 Header
if (aMessage.GetOffset() == 0)
{
hcLength = netif.GetLowpan().Compress(aMessage, meshSource, meshDest, payload);
assert(hcLength > 0);
headerLength += static_cast<uint8_t>(hcLength);
payloadLength = aMessage.GetLength() - aMessage.GetOffset();
fragmentLength = aFrame.GetMaxPayloadLength() - headerLength;
if (payloadLength > fragmentLength)
{
if ((!aMessage.IsLinkSecurityEnabled()) && aMessage.IsSubTypeMle())
{
aMessage.SetOffset(0);
ExitNow(error = OT_ERROR_NOT_CAPABLE);
}
// write Fragment header
if (aMessage.GetDatagramTag() == 0)
{
// avoid using datagram tag value 0, which indicates the tag has not been set
if (mFragTag == 0)
{
mFragTag++;
}
aMessage.SetDatagramTag(mFragTag++);
}
memmove(payload + 4, payload, headerLength);
payloadLength = (aFrame.GetMaxPayloadLength() - headerLength - 4) & ~0x7;
fragmentHeader = reinterpret_cast<Lowpan::FragmentHeader *>(payload);
fragmentHeader->Init();
fragmentHeader->SetDatagramSize(aMessage.GetLength());
fragmentHeader->SetDatagramTag(aMessage.GetDatagramTag());
fragmentHeader->SetDatagramOffset(0);
payload += fragmentHeader->GetHeaderLength();
headerLength += fragmentHeader->GetHeaderLength();
}
payload += hcLength;
// copy IPv6 Payload
aMessage.Read(aMessage.GetOffset(), payloadLength, payload);
aFrame.SetPayloadLength(static_cast<uint8_t>(headerLength + payloadLength));
mMessageNextOffset = aMessage.GetOffset() + payloadLength;
aMessage.SetOffset(0);
}
else
{
payloadLength = aMessage.GetLength() - aMessage.GetOffset();
// write Fragment header
fragmentHeader = reinterpret_cast<Lowpan::FragmentHeader *>(payload);
fragmentHeader->Init();
fragmentHeader->SetDatagramSize(aMessage.GetLength());
fragmentHeader->SetDatagramTag(aMessage.GetDatagramTag());
fragmentHeader->SetDatagramOffset(aMessage.GetOffset());
payload += fragmentHeader->GetHeaderLength();
headerLength += fragmentHeader->GetHeaderLength();
fragmentLength = (aFrame.GetMaxPayloadLength() - headerLength) & ~0x7;
if (payloadLength > fragmentLength)
{
payloadLength = fragmentLength;
}
// copy IPv6 Payload
aMessage.Read(aMessage.GetOffset(), payloadLength, payload);
aFrame.SetPayloadLength(static_cast<uint8_t>(headerLength + payloadLength));
mMessageNextOffset = aMessage.GetOffset() + payloadLength;
}
if (mMessageNextOffset < aMessage.GetLength())
{
aFrame.SetFramePending(true);
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
aMessage.SetTimeSync(false);
#endif
}
exit:
return error;
}
otError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame, bool aAckRequest)
{
ThreadNetif &netif = GetNetif();
uint16_t fcf;
uint8_t secCtl;
Mac::Address macSource;
macSource.SetShort(netif.GetMac().GetShortAddress());
if (macSource.IsShortAddrInvalid())
{
macSource.SetExtended(netif.GetMac().GetExtAddress());
}
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006;
fcf |= (mMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (macSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
if (aAckRequest)
{
fcf |= Mac::Frame::kFcfAckRequest;
}
fcf |= Mac::Frame::kFcfSecurityEnabled;
secCtl = Mac::Frame::kKeyIdMode1;
secCtl |= Mac::Frame::kSecEncMic32;
fcf |= Mac::Frame::kFcfPanidCompression;
aFrame.InitMacHeader(fcf, secCtl);
aFrame.SetDstPanId(netif.GetMac().GetPanId());
aFrame.SetSrcPanId(netif.GetMac().GetPanId());
aFrame.SetDstAddr(mMacDest);
aFrame.SetSrcAddr(macSource);
aFrame.SetPayloadLength(0);
aFrame.SetFramePending(false);
return OT_ERROR_NONE;
}
void MeshForwarder::HandleSentFrame(Mac::Sender &aSender, Mac::Frame &aFrame, otError aError)
{
aSender.GetOwner<MeshForwarder>().HandleSentFrame(aFrame, aError);
}
void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError)
{
ThreadNetif &netif = GetNetif();
Mac::Address macDest;
Neighbor * neighbor;
mSendBusy = false;
VerifyOrExit(mEnabled);
aFrame.GetDstAddr(macDest);
if ((neighbor = netif.GetMle().GetNeighbor(macDest)) != NULL)
{
switch (aError)
{
case OT_ERROR_NONE:
if (aFrame.GetAckRequest())
{
neighbor->ResetLinkFailures();
}
break;
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
case OT_ERROR_ABORT:
break;
case OT_ERROR_NO_ACK:
neighbor->IncrementLinkFailures();
if (netif.GetMle().IsActiveRouter(neighbor->GetRloc16()))
{
if (neighbor->GetLinkFailures() >= Mle::kFailedRouterTransmissions)
{
netif.GetMle().RemoveNeighbor(*neighbor);
}
}
break;
default:
assert(false);
break;
}
}
HandleSentFrameToChild(aFrame, aError, macDest);
VerifyOrExit(mSendMessage != NULL);
if (mSendMessage->GetDirectTransmission())
{
if (aError != OT_ERROR_NONE)
{
// If the transmission of any fragment frame fails,
// the overall message transmission is considered
// as failed
mSendMessage->SetTxSuccess(false);
#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// We set the NextOffset to end of message to avoid sending
// any remaining fragments in the message.
mMessageNextOffset = mSendMessage->GetLength();
#endif
}
if (mMessageNextOffset < mSendMessage->GetLength())
{
mSendMessage->SetOffset(mMessageNextOffset);
}
else
{
otError txError = aError;
mSendMessage->ClearDirectTransmission();
mSendMessage->SetOffset(0);
if (neighbor != NULL)
{
neighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess());
}
#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 `GetTxSuccess() tracks the tx success of the
// entire message, while `aError` represents the error
// status of the last fragment frame transmission.
if (!mSendMessage->GetTxSuccess() && (txError == OT_ERROR_NONE))
{
txError = OT_ERROR_FAILED;
}
#endif
LogMessage(kMessageTransmit, *mSendMessage, &macDest, txError);
if (mSendMessage->GetType() == Message::kTypeIp6)
{
if (mSendMessage->GetTxSuccess())
{
mIpCounters.mTxSuccess++;
}
else
{
mIpCounters.mTxFailure++;
}
}
}
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
mSendBusy = true;
mDiscoverTimer.Start(static_cast<uint16_t>(Mac::kScanDurationDefault));
ExitNow();
}
}
if (mSendMessage->GetType() == Message::kTypeMacDataPoll)
{
neighbor = netif.GetMle().GetParentCandidate();
if (neighbor->GetState() == Neighbor::kStateInvalid)
{
mDataPollManager.StopPolling();
netif.GetMle().BecomeDetached();
}
else
{
mDataPollManager.HandlePollSent(aError);
}
}
if (mSendMessage->GetDirectTransmission() == false && mSendMessage->IsChildPending() == false)
{
mSendQueue.Dequeue(*mSendMessage);
mSendMessage->Free();
mSendMessage = NULL;
mMessageNextOffset = 0;
}
exit:
if (mEnabled)
{
mScheduleTransmissionTask.Post();
}
}
void MeshForwarder::SetDiscoverParameters(uint32_t aScanChannels)
{
mScanChannels = (aScanChannels == 0) ? static_cast<uint32_t>(Mac::kScanChannelsAll) : aScanChannels;
}
void MeshForwarder::HandleDiscoverTimer(Timer &aTimer)
{
aTimer.GetOwner<MeshForwarder>().HandleDiscoverTimer();
}
void MeshForwarder::HandleDiscoverTimer(void)
{
do
{
mScanChannels >>= 1;
mScanChannel++;
if (mScanChannel > OT_RADIO_CHANNEL_MAX)
{
mSendQueue.Dequeue(*mSendMessage);
mSendMessage->Free();
mSendMessage = NULL;
HandleDiscoverComplete();
ExitNow();
}
} while ((mScanChannels & 1) == 0);
mSendMessage->SetDirectTransmission();
exit:
mSendBusy = false;
mScheduleTransmissionTask.Post();
}
void MeshForwarder::HandleDiscoverComplete(void)
{
ThreadNetif &netif = GetNetif();
assert(mScanning);
if (mMacRadioAcquisitionId)
{
netif.GetMac().ReleaseRadioChannel();
mMacRadioAcquisitionId = 0;
}
netif.GetMac().SetPanId(mRestorePanId);
mScanning = false;
netif.GetMle().HandleDiscoverComplete();
mDiscoverTimer.Stop();
}
void MeshForwarder::HandleReceivedFrame(Mac::Receiver &aReceiver, Mac::Frame &aFrame)
{
aReceiver.GetOwner<MeshForwarder>().HandleReceivedFrame(aFrame);
}
void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame)
{
ThreadNetif & netif = GetNetif();
otThreadLinkInfo linkInfo;
Mac::Address macDest;
Mac::Address macSource;
uint8_t * payload;
uint8_t payloadLength;
otError error = OT_ERROR_NONE;
if (!mEnabled)
{
ExitNow(error = OT_ERROR_INVALID_STATE);
}
SuccessOrExit(error = aFrame.GetSrcAddr(macSource));
SuccessOrExit(error = aFrame.GetDstAddr(macDest));
aFrame.GetSrcPanId(linkInfo.mPanId);
linkInfo.mChannel = aFrame.GetChannel();
linkInfo.mRss = aFrame.GetRssi();
linkInfo.mLqi = aFrame.GetLqi();
linkInfo.mLinkSecurity = aFrame.GetSecurityEnabled();
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
linkInfo.mNetworkTimeOffset = aFrame.GetNetworkTimeOffset();
linkInfo.mTimeSyncSeq = aFrame.GetTimeSyncSeq();
#endif
payload = aFrame.GetPayload();
payloadLength = aFrame.GetPayloadLength();
netif.GetSupervisionListener().UpdateOnReceive(macSource, linkInfo.mLinkSecurity);
switch (aFrame.GetType())
{
case Mac::Frame::kFcfFrameData:
if (payloadLength >= sizeof(Lowpan::MeshHeader) &&
reinterpret_cast<Lowpan::MeshHeader *>(payload)->IsMeshHeader())
{
HandleMesh(payload, payloadLength, macSource, linkInfo);
}
else if (payloadLength >= sizeof(Lowpan::FragmentHeader) &&
reinterpret_cast<Lowpan::FragmentHeader *>(payload)->IsFragmentHeader())
{
HandleFragment(payload, payloadLength, macSource, macDest, linkInfo);
}
else if (payloadLength >= 1 && Lowpan::Lowpan::IsLowpanHc(payload))
{
HandleLowpanHC(payload, payloadLength, macSource, macDest, linkInfo);
}
else
{
VerifyOrExit(payloadLength == 0, error = OT_ERROR_NOT_LOWPAN_DATA_FRAME);
LogFrame("Received empty payload frame", aFrame, OT_ERROR_NONE);
}
break;
#if OPENTHREAD_FTD
case Mac::Frame::kFcfFrameMacCmd:
{
uint8_t commandId;
aFrame.GetCommandId(commandId);
if (commandId == Mac::Frame::kMacCmdDataRequest)
{
HandleDataRequest(macSource, linkInfo);
}
else
{
error = OT_ERROR_DROP;
}
break;
}
#endif
case Mac::Frame::kFcfFrameBeacon:
break;
default:
error = OT_ERROR_DROP;
break;
}
exit:
if (error != OT_ERROR_NONE)
{
LogFrame("Dropping rx frame", aFrame, error);
}
}
void MeshForwarder::HandleFragment(uint8_t * aFrame,
uint8_t aFrameLength,
const Mac::Address & aMacSource,
const Mac::Address & aMacDest,
const otThreadLinkInfo &aLinkInfo)
{
ThreadNetif & netif = GetNetif();
otError error = OT_ERROR_NONE;
Lowpan::FragmentHeader fragmentHeader;
Message * message = NULL;
int headerLength;
// Check the fragment header
VerifyOrExit(fragmentHeader.Init(aFrame, aFrameLength) == OT_ERROR_NONE, error = OT_ERROR_DROP);
aFrame += fragmentHeader.GetHeaderLength();
aFrameLength -= fragmentHeader.GetHeaderLength();
if (fragmentHeader.GetDatagramOffset() == 0)
{
VerifyOrExit((message = GetInstance().GetMessagePool().New(Message::kTypeIp6, 0)) != NULL,
error = OT_ERROR_NO_BUFS);
message->SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
message->SetPanId(aLinkInfo.mPanId);
message->AddRss(aLinkInfo.mRss);
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
message->SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq);
message->SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset);
#endif
headerLength = netif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength,
fragmentHeader.GetDatagramSize());
VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE);
aFrame += headerLength;
aFrameLength -= static_cast<uint8_t>(headerLength);
VerifyOrExit(fragmentHeader.GetDatagramSize() >= message->GetOffset() + aFrameLength, error = OT_ERROR_PARSE);
SuccessOrExit(error = message->SetLength(fragmentHeader.GetDatagramSize()));
message->SetDatagramTag(fragmentHeader.GetDatagramTag());
message->SetTimeout(kReassemblyTimeout);
// copy Fragment
message->Write(message->GetOffset(), aFrameLength, aFrame);
message->MoveOffset(aFrameLength);
// Security Check
VerifyOrExit(netif.GetIp6Filter().Accept(*message), error = OT_ERROR_DROP);
// Allow re-assembly of only one message at a time on a SED by clearing
// any remaining fragments in reassembly list upon receiving of a new
// (secure) first fragment.
if ((GetRxOnWhenIdle() == false) && message->IsLinkSecurityEnabled())
{
ClearReassemblyList();
}
mReassemblyList.Enqueue(*message);
if (!mReassemblyTimer.IsRunning())
{
mReassemblyTimer.Start(kStateUpdatePeriod);
}
}
else
{
for (message = mReassemblyList.GetHead(); message; message = message->GetNext())
{
// Security Check: only consider reassembly buffers that had the same Security Enabled setting.
if (message->GetLength() == fragmentHeader.GetDatagramSize() &&
message->GetDatagramTag() == fragmentHeader.GetDatagramTag() &&
message->GetOffset() == fragmentHeader.GetDatagramOffset() &&
message->GetOffset() + aFrameLength <= fragmentHeader.GetDatagramSize() &&
message->IsLinkSecurityEnabled() == aLinkInfo.mLinkSecurity)
{
break;
}
}
// For a sleepy-end-device, if we receive a new (secure) next fragment
// with a non-matching fragmentation offset or tag, it indicates that
// we have either missed a fragment, or the parent has moved to a new
// message with a new tag. In either case, we can safely clear any
// remaining fragments stored in the reassembly list.
if (GetRxOnWhenIdle() == false)
{
if ((message == NULL) && (aLinkInfo.mLinkSecurity))
{
ClearReassemblyList();
}
}
VerifyOrExit(message != NULL, error = OT_ERROR_DROP);
// copy Fragment
message->Write(message->GetOffset(), aFrameLength, aFrame);
message->MoveOffset(aFrameLength);
message->AddRss(aLinkInfo.mRss);
}
exit:
if (error == OT_ERROR_NONE)
{
if (message->GetOffset() >= message->GetLength())
{
mReassemblyList.Dequeue(*message);
HandleDatagram(*message, aLinkInfo, aMacSource);
}
}
else
{
LogFragmentFrameDrop(error, aFrameLength, aMacSource, aMacDest, fragmentHeader, aLinkInfo.mLinkSecurity);
if (message != NULL)
{
message->Free();
}
}
}
void MeshForwarder::ClearReassemblyList(void)
{
Message *message;
Message *next;
for (message = mReassemblyList.GetHead(); message; message = next)
{
next = message->GetNext();
mReassemblyList.Dequeue(*message);
LogMessage(kMessageReassemblyDrop, *message, NULL, OT_ERROR_NO_FRAME_RECEIVED);
if (message->GetType() == Message::kTypeIp6)
{
mIpCounters.mRxFailure++;
}
message->Free();
}
}
void MeshForwarder::HandleReassemblyTimer(Timer &aTimer)
{
aTimer.GetOwner<MeshForwarder>().HandleReassemblyTimer();
}
void MeshForwarder::HandleReassemblyTimer(void)
{
Message *next = NULL;
uint8_t timeout;
for (Message *message = mReassemblyList.GetHead(); message; message = next)
{
next = message->GetNext();
timeout = message->GetTimeout();
if (timeout > 0)
{
message->SetTimeout(timeout - 1);
}
else
{
mReassemblyList.Dequeue(*message);
LogMessage(kMessageReassemblyDrop, *message, NULL, OT_ERROR_REASSEMBLY_TIMEOUT);
if (message->GetType() == Message::kTypeIp6)
{
mIpCounters.mRxFailure++;
}
message->Free();
}
}
if (mReassemblyList.GetHead() != NULL)
{
mReassemblyTimer.Start(kStateUpdatePeriod);
}
}
void MeshForwarder::HandleLowpanHC(uint8_t * aFrame,
uint8_t aFrameLength,
const Mac::Address & aMacSource,
const Mac::Address & aMacDest,
const otThreadLinkInfo &aLinkInfo)
{
ThreadNetif &netif = GetNetif();
otError error = OT_ERROR_NONE;
Message * message = NULL;
int headerLength;
#if OPENTHREAD_FTD
UpdateRoutes(aFrame, aFrameLength, aMacSource, aMacDest);
#endif
VerifyOrExit((message = GetInstance().GetMessagePool().New(Message::kTypeIp6, 0)) != NULL,
error = OT_ERROR_NO_BUFS);
message->SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
message->SetPanId(aLinkInfo.mPanId);
message->AddRss(aLinkInfo.mRss);
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
message->SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq);
message->SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset);
#endif
headerLength = netif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, 0);
VerifyOrExit(headerLength > 0, error = OT_ERROR_PARSE);
aFrame += headerLength;
aFrameLength -= static_cast<uint8_t>(headerLength);
SuccessOrExit(error = message->SetLength(message->GetLength() + aFrameLength));
message->Write(message->GetOffset(), aFrameLength, aFrame);
// Security Check
VerifyOrExit(netif.GetIp6Filter().Accept(*message), error = OT_ERROR_DROP);
exit:
if (error == OT_ERROR_NONE)
{
HandleDatagram(*message, aLinkInfo, aMacSource);
}
else
{
LogLowpanHcFrameDrop(error, aFrameLength, aMacSource, aMacDest, aLinkInfo.mLinkSecurity);
if (message != NULL)
{
message->Free();
}
}
}
otError MeshForwarder::HandleDatagram(Message & aMessage,
const otThreadLinkInfo &aLinkInfo,
const Mac::Address & aMacSource)
{
ThreadNetif &netif = GetNetif();
LogMessage(kMessageReceive, aMessage, &aMacSource, OT_ERROR_NONE);
if (aMessage.GetType() == Message::kTypeIp6)
{
mIpCounters.mRxSuccess++;
}
return netif.GetIp6().HandleDatagram(aMessage, &netif, netif.GetInterfaceId(), &aLinkInfo, false);
}
void MeshForwarder::HandleDataPollTimeout(Mac::Receiver &aReceiver)
{
aReceiver.GetOwner<MeshForwarder>().GetDataPollManager().HandlePollTimeout();
}
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
otError MeshForwarder::ParseIp6UdpTcpHeader(const Message &aMessage,
Ip6::Header & aIp6Header,
uint16_t & aChecksum,
uint16_t & aSourcePort,
uint16_t & aDestPort)
{
otError error = OT_ERROR_PARSE;
union
{
Ip6::UdpHeader udp;
Ip6::TcpHeader tcp;
} header;
aChecksum = 0;
aSourcePort = 0;
aDestPort = 0;
VerifyOrExit(sizeof(Ip6::Header) == aMessage.Read(0, sizeof(Ip6::Header), &aIp6Header));
VerifyOrExit(aIp6Header.IsVersion6());
switch (aIp6Header.GetNextHeader())
{
case Ip6::kProtoUdp:
VerifyOrExit(sizeof(Ip6::UdpHeader) == aMessage.Read(sizeof(Ip6::Header), 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(sizeof(Ip6::Header), 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;
}
const char *MeshForwarder::MessageActionToString(MessageAction aAction, otError aError)
{
const char *actionText = "";
switch (aAction)
{
case kMessageReceive:
actionText = "Received";
break;
case kMessageTransmit:
actionText = (aError == OT_ERROR_NONE) ? "Sent" : "Failed to send";
break;
case kMessagePrepareIndirect:
actionText = "Prepping indir tx";
break;
case kMessageDrop:
actionText = "Dropping";
break;
case kMessageReassemblyDrop:
actionText = "Dropping (reassembly queue)";
break;
case kMessageEvict:
actionText = "Evicting";
break;
}
return actionText;
}
const char *MeshForwarder::MessagePriorityToString(const Message &aMessage)
{
const char *priorityText = "unknown";
switch (aMessage.GetPriority())
{
case Message::kPriorityHigh:
priorityText = "high";
break;
case Message::kPriorityMedium:
priorityText = "medium";
break;
case Message::kPriorityLow:
priorityText = "low";
break;
case Message::kPriorityVeryLow:
priorityText = "verylow";
break;
}
return priorityText;
}
#if OPENTHREAD_CONFIG_LOG_SRC_DST_IP_ADDRESSES
void MeshForwarder::LogIp6SourceDestAddresses(Ip6::Header &aIp6Header,
uint16_t aSourcePort,
uint16_t aDestPort,
otLogLevel aLogLevel)
{
if (aSourcePort != 0)
{
otLogMac(GetInstance(), aLogLevel, "\tsrc:[%s]:%d", aIp6Header.GetSource().ToString().AsCString(), aSourcePort);
}
else
{
otLogMac(GetInstance(), aLogLevel, "\tsrc:[%s]", aIp6Header.GetSource().ToString().AsCString());
}
if (aDestPort != 0)
{
otLogMac(GetInstance(), aLogLevel, "\tdst:[%s]:%d", aIp6Header.GetDestination().ToString().AsCString(),
aDestPort);
}
else
{
otLogMac(GetInstance(), aLogLevel, "\tdst:[%s]", aIp6Header.GetDestination().ToString().AsCString());
}
}
#else
void MeshForwarder::LogIp6SourceDestAddresses(Ip6::Header &, uint16_t, uint16_t, otLogLevel)
{
}
#endif
void MeshForwarder::LogIp6Message(MessageAction aAction,
const Message & aMessage,
const Mac::Address *aMacAddress,
otError aError,
otLogLevel aLogLevel)
{
Ip6::Header ip6Header;
uint16_t checksum;
uint16_t sourcePort;
uint16_t destPort;
bool shouldLogRss;
SuccessOrExit(ParseIp6UdpTcpHeader(aMessage, ip6Header, checksum, sourcePort, destPort));
shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop);
otLogMac(GetInstance(), aLogLevel, "%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s%s%s",
MessageActionToString(aAction, aError), Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()),
aMessage.GetLength(), checksum,
(aMacAddress == NULL) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
(aMacAddress == NULL) ? "" : aMacAddress->ToString().AsCString(),
aMessage.IsLinkSecurityEnabled() ? "yes" : "no", (aError == OT_ERROR_NONE) ? "" : ", error:",
(aError == OT_ERROR_NONE) ? "" : otThreadErrorToString(aError), MessagePriorityToString(aMessage),
shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "");
if (aAction != kMessagePrepareIndirect)
{
LogIp6SourceDestAddresses(ip6Header, sourcePort, destPort, aLogLevel);
}
exit:
return;
}
void MeshForwarder::LogMessage(MessageAction aAction,
const Message & aMessage,
const Mac::Address *aMacAddress,
otError aError)
{
otLogLevel logLevel = OT_LOG_LEVEL_INFO;
switch (aAction)
{
case kMessageReceive:
case kMessageTransmit:
case kMessagePrepareIndirect:
logLevel = (aError == OT_ERROR_NONE) ? OT_LOG_LEVEL_INFO : OT_LOG_LEVEL_NOTE;
break;
case kMessageDrop:
case kMessageReassemblyDrop:
case kMessageEvict:
logLevel = OT_LOG_LEVEL_NOTE;
break;
}
VerifyOrExit(GetInstance().GetLogLevel() >= logLevel);
switch (aMessage.GetType())
{
case Message::kTypeIp6:
LogIp6Message(aAction, aMessage, aMacAddress, aError, logLevel);
break;
#if OPENTHREAD_FTD
case Message::kType6lowpan:
LogMeshMessage(aAction, aMessage, aMacAddress, aError, logLevel);
break;
#endif
default:
break;
}
exit:
return;
}
void MeshForwarder::LogFrame(const char *aActionText, const Mac::Frame &aFrame, otError aError)
{
if (aError != OT_ERROR_NONE)
{
otLogNoteMac(GetInstance(), "%s, aError:%s, %s", aActionText, otThreadErrorToString(aError),
aFrame.ToInfoString().AsCString());
}
else
{
otLogInfoMac(GetInstance(), "%s, %s", aActionText, aFrame.ToInfoString().AsCString());
}
}
void MeshForwarder::LogFragmentFrameDrop(otError aError,
uint8_t aFrameLength,
const Mac::Address & aMacSource,
const Mac::Address & aMacDest,
const Lowpan::FragmentHeader &aFragmentHeader,
bool aIsSecure)
{
otLogNoteMac(GetInstance(),
"Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s",
otThreadErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(),
aMacDest.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(),
aFragmentHeader.GetDatagramSize(), aIsSecure ? "yes" : "no");
}
void MeshForwarder::LogLowpanHcFrameDrop(otError aError,
uint8_t aFrameLength,
const Mac::Address &aMacSource,
const Mac::Address &aMacDest,
bool aIsSecure)
{
otLogNoteMac(GetInstance(), "Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s",
otThreadErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(),
aMacDest.ToString().AsCString(), aIsSecure ? "yes" : "no");
}
#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
void MeshForwarder::LogMessage(MessageAction, const Message &, const Mac::Address *, otError)
{
}
void MeshForwarder::LogFrame(const char *, const Mac::Frame &, otError)
{
}
void MeshForwarder::LogFragmentFrameDrop(otError,
uint8_t,
const Mac::Address &,
const Mac::Address &,
const Lowpan::FragmentHeader &,
bool)
{
}
void MeshForwarder::LogLowpanHcFrameDrop(otError, uint8_t, const Mac::Address &, const Mac::Address &, bool)
{
}
#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
} // namespace ot