blob: 12f93c2b26f5021686db8f1335c227a11970f689 [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 <common/code_utils.hpp>
#include <common/debug.hpp>
#include <common/logging.hpp>
#include <common/encoding.hpp>
#include <common/message.hpp>
#include <net/ip6.hpp>
#include <net/ip6_filter.hpp>
#include <net/udp6.hpp>
#include <net/netif.hpp>
#include <net/udp6.hpp>
#include <platform/random.h>
#include <thread/mesh_forwarder.hpp>
#include <thread/mle_router.hpp>
#include <thread/mle.hpp>
#include <thread/thread_netif.hpp>
using Thread::Encoding::BigEndian::HostSwap16;
namespace Thread {
MeshForwarder::MeshForwarder(ThreadNetif &aThreadNetif):
mMacReceiver(&MeshForwarder::HandleReceivedFrame, this),
mMacSender(&MeshForwarder::HandleFrameRequest, &MeshForwarder::HandleSentFrame, this),
mDiscoverTimer(aThreadNetif.GetIp6().mTimerScheduler, &MeshForwarder::HandleDiscoverTimer, this),
mPollTimer(aThreadNetif.GetIp6().mTimerScheduler, &MeshForwarder::HandlePollTimer, this),
mReassemblyTimer(aThreadNetif.GetIp6().mTimerScheduler, &MeshForwarder::HandleReassemblyTimer, this),
mMessageNextOffset(0),
mPollPeriod(0),
mAssignPollPeriod(0),
mSendMessage(NULL),
mMeshSource(Mac::kShortAddrInvalid),
mMeshDest(Mac::kShortAddrInvalid),
mAddMeshHeader(false),
mSendBusy(false),
mScheduleTransmissionTask(aThreadNetif.GetIp6().mTaskletScheduler, ScheduleTransmissionTask, this),
mEnabled(false),
mScanChannels(0),
mScanDuration(0),
mScanChannel(0),
mRestoreChannel(0),
mRestorePanId(Mac::kPanIdBroadcast),
mScanning(false),
mNetif(aThreadNetif),
mSrcMatchEnabled(false)
{
mFragTag = static_cast<uint16_t>(otPlatRandomGet());
mNetif.GetMac().RegisterReceiver(mMacReceiver);
mMacSource.mLength = 0;
mMacDest.mLength = 0;
}
ThreadError MeshForwarder::Start(void)
{
ThreadError error = kThreadError_None;
if (mEnabled == false)
{
mNetif.GetMac().SetRxOnWhenIdle(true);
mEnabled = true;
}
return error;
}
ThreadError MeshForwarder::Stop(void)
{
ThreadError error = kThreadError_None;
Message *message;
VerifyOrExit(mEnabled == true,);
mPollTimer.Stop();
mReassemblyTimer.Stop();
if (mScanning)
{
mNetif.GetMac().SetChannel(mRestoreChannel);
mScanning = false;
mNetif.GetMle().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;
mNetif.GetMac().SetRxOnWhenIdle(false);
exit:
return error;
}
void MeshForwarder::HandleResolved(const Ip6::Address &aEid, ThreadError 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 (memcmp(&ip6Dst, &aEid, sizeof(ip6Dst)) == 0)
{
mResolvingQueue.Dequeue(*cur);
if (aError == kThreadError_None)
{
mSendQueue.Enqueue(*cur);
enqueuedMessage = true;
}
else
{
cur->Free();
}
}
}
if (enqueuedMessage)
{
mScheduleTransmissionTask.Post();
}
}
void MeshForwarder::ScheduleTransmissionTask(void *aContext)
{
static_cast<MeshForwarder *>(aContext)->ScheduleTransmissionTask();
}
void MeshForwarder::ClearChildIndirectMessages(Child &aChild)
{
Message *nextMessage;
VerifyOrExit(aChild.mQueuedIndirectMessageCnt > 0,);
for (Message *message = mSendQueue.GetHead(); message; message = nextMessage)
{
nextMessage = message->GetNext();
message->ClearChildMask(mNetif.GetMle().GetChildIndex(aChild));
if (!message->IsChildPending())
{
if (mSendMessage == message)
{
mSendMessage = NULL;
}
mSendQueue.Dequeue(*message);
message->Free();
}
}
aChild.mQueuedIndirectMessageCnt = 0;
ClearSrcMatchEntry(aChild);
exit:
return;
}
void MeshForwarder::UpdateIndirectMessages(void)
{
Child *children;
uint8_t numChildren;
children = mNetif.GetMle().GetChildren(&numChildren);
for (uint8_t i = 0; i < numChildren; i++)
{
Child *child = &children[i];
if (child->mState == Child::kStateValid || child->mQueuedIndirectMessageCnt == 0)
{
continue;
}
ClearChildIndirectMessages(*child);
}
}
void MeshForwarder::ScheduleTransmissionTask()
{
ThreadError error = kThreadError_None;
uint8_t numChildren;
Child *children;
VerifyOrExit(mSendBusy == false, error = kThreadError_Busy);
UpdateIndirectMessages();
children = mNetif.GetMle().GetChildren(&numChildren);
for (int i = 0; i < numChildren; i++)
{
if (children[i].mState == Child::kStateValid &&
children[i].mDataRequest)
{
mSendMessage = GetIndirectTransmission(children[i]);
if (mSendMessage != NULL)
{
mSendMessage->SetOffset(children[i].mFragmentOffset);
}
else
{
if (children[i].mAddSrcMatchEntryShort)
{
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = children[i].mValid.mRloc16;
}
else
{
mMacDest.mLength = sizeof(mMacDest.mExtAddress);
memcpy(mMacDest.mExtAddress.m8, children[i].mMacAddr.m8, sizeof(mMacDest.mExtAddress));
}
}
mNetif.GetMac().SendFrameRequest(mMacSender);
ExitNow();
}
}
if ((mSendMessage = GetDirectTransmission()) != NULL)
{
mNetif.GetMac().SendFrameRequest(mMacSender);
ExitNow();
}
exit:
(void) error;
}
ThreadError MeshForwarder::AddPendingSrcMatchEntries(void)
{
uint8_t numChildren;
Child *children = NULL;
ThreadError error = kThreadError_NoBufs;
children = mNetif.GetMle().GetChildren(&numChildren);
// Add pending short address first
for (uint8_t i = 0; i < numChildren; i++)
{
if (children[i].mState == Child::kStateValid &&
children[i].mAddSrcMatchEntryPending &&
children[i].mAddSrcMatchEntryShort)
{
VerifyOrExit(((error = AddSrcMatchEntry(children[i])) == kThreadError_None), ;);
}
}
// Add pending extended address
for (uint8_t i = 0; i < numChildren; i++)
{
if (children[i].mState == Child::kStateValid &&
children[i].mAddSrcMatchEntryPending &&
!children[i].mAddSrcMatchEntryShort)
{
VerifyOrExit(((error = AddSrcMatchEntry(children[i])) == kThreadError_None), ;);
}
}
exit:
return error;
}
ThreadError MeshForwarder::AddSrcMatchEntry(Child &aChild)
{
ThreadError error = kThreadError_NoBufs;
Mac::Address macAddr;
otLogDebgMac("Queuing for child (0x%x)", aChild.mValid.mRloc16);
otLogDebgMac("SrcMatch %d (0:Dis, 1:En))", mSrcMatchEnabled);
// first queued message, to be added into source match table
if (aChild.mQueuedIndirectMessageCnt == 1)
{
aChild.mAddSrcMatchEntryPending = true;
}
VerifyOrExit(aChild.mAddSrcMatchEntryPending, ;);
if (aChild.mAddSrcMatchEntryShort)
{
macAddr.mLength = sizeof(macAddr.mShortAddress);
macAddr.mShortAddress = aChild.mValid.mRloc16;
}
else
{
macAddr.mLength = sizeof(macAddr.mExtAddress);
memcpy(macAddr.mExtAddress.m8, aChild.mMacAddr.m8, sizeof(macAddr.mExtAddress));
}
if ((error = mNetif.GetMac().AddSrcMatchEntry(macAddr)) == kThreadError_None)
{
// succeed in adding to source match table
aChild.mAddSrcMatchEntryPending = false;
if (!mSrcMatchEnabled)
{
mNetif.GetMac().EnableSrcMatch(true);
mSrcMatchEnabled = true;
}
}
else
{
if (mSrcMatchEnabled)
{
mNetif.GetMac().EnableSrcMatch(false);
mSrcMatchEnabled = false;
}
}
exit:
return error;
}
void MeshForwarder::ClearSrcMatchEntry(Child &aChild)
{
Mac::Address macAddr;
otLogDebgMac("SrcMatch %d (0:Dis, 1:En))", mSrcMatchEnabled);
if (aChild.mAddSrcMatchEntryShort)
{
macAddr.mLength = sizeof(macAddr.mShortAddress);
macAddr.mShortAddress = aChild.mValid.mRloc16;
}
else
{
macAddr.mLength = sizeof(macAddr.mExtAddress);
memcpy(macAddr.mExtAddress.m8, aChild.mMacAddr.m8, sizeof(macAddr.mExtAddress));
}
if (mNetif.GetMac().ClearSrcMatchEntry(macAddr) == kThreadError_None)
{
if (!mSrcMatchEnabled && (AddPendingSrcMatchEntries() == kThreadError_None))
{
mNetif.GetMac().EnableSrcMatch(true);
mSrcMatchEnabled = true;
}
}
else
{
// if finished queued messages for SED which is not added into the source match table
aChild.mAddSrcMatchEntryPending = false;
}
}
ThreadError MeshForwarder::SendMessage(Message &aMessage)
{
ThreadError error = kThreadError_None;
Neighbor *neighbor;
uint8_t numChildren;
Child *children;
switch (aMessage.GetType())
{
case Message::kTypeIp6:
{
Ip6::Header ip6Header;
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
if (!memcmp(&ip6Header.GetDestination(), mNetif.GetMle().GetLinkLocalAllThreadNodesAddress(),
sizeof(ip6Header.GetDestination())) ||
!memcmp(&ip6Header.GetDestination(), mNetif.GetMle().GetRealmLocalAllThreadNodesAddress(),
sizeof(ip6Header.GetDestination())))
{
// schedule direct transmission
aMessage.SetDirectTransmission();
if (aMessage.GetSubType() != Message::kSubTypeMplRetransmission)
{
// destined for all sleepy children
children = mNetif.GetMle().GetChildren(&numChildren);
for (uint8_t i = 0; i < numChildren; i++)
{
if (children[i].mState == Neighbor::kStateValid && (children[i].mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
{
children[i].mQueuedIndirectMessageCnt++;
AddSrcMatchEntry(children[i]);
aMessage.SetChildMask(i);
}
}
}
}
else if ((neighbor = mNetif.GetMle().GetNeighbor(ip6Header.GetDestination())) != NULL &&
(neighbor->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
{
// destined for a sleepy child
children = static_cast<Child *>(neighbor);
children->mQueuedIndirectMessageCnt++;
AddSrcMatchEntry(*children);
aMessage.SetChildMask(mNetif.GetMle().GetChildIndex(*children));
}
else
{
// schedule direct transmission
aMessage.SetDirectTransmission();
}
break;
}
case Message::kType6lowpan:
{
Lowpan::MeshHeader meshHeader;
IgnoreReturnValue(meshHeader.Init(aMessage));
if ((neighbor = mNetif.GetMle().GetNeighbor(meshHeader.GetDestination())) != NULL &&
(neighbor->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
{
// destined for a sleepy child
children = static_cast<Child *>(neighbor);
children->mQueuedIndirectMessageCnt++;
AddSrcMatchEntry(*children);
aMessage.SetChildMask(mNetif.GetMle().GetChildIndex(*children));
}
else
{
// not destined for a sleepy child
aMessage.SetDirectTransmission();
}
break;
}
case Message::kTypeMacDataPoll:
aMessage.SetDirectTransmission();
break;
}
aMessage.SetOffset(0);
aMessage.SetDatagramTag(0);
SuccessOrExit(error = mSendQueue.Enqueue(aMessage));
mScheduleTransmissionTask.Post();
exit:
return error;
}
Message *MeshForwarder::GetDirectTransmission()
{
Message *curMessage, *nextMessage;
ThreadError error = kThreadError_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);
break;
case Message::kType6lowpan:
error = UpdateMeshRoute(*curMessage);
break;
case Message::kTypeMacDataPoll:
ExitNow();
}
switch (error)
{
case kThreadError_None:
ExitNow();
case kThreadError_AddressQuery:
mSendQueue.Dequeue(*curMessage);
mResolvingQueue.Enqueue(*curMessage);
continue;
case kThreadError_Drop:
case kThreadError_NoBufs:
mSendQueue.Dequeue(*curMessage);
curMessage->Free();
continue;
default:
assert(false);
break;
}
}
exit:
return curMessage;
}
Message *MeshForwarder::GetIndirectTransmission(const Child &aChild)
{
Message *message = NULL;
uint8_t childIndex = mNetif.GetMle().GetChildIndex(aChild);
Ip6::Header ip6Header;
for (message = mSendQueue.GetHead(); message; message = message->GetNext())
{
if (message->GetChildMask(childIndex))
{
break;
}
}
VerifyOrExit(message != NULL, ;);
switch (message->GetType())
{
case Message::kTypeIp6:
message->Read(0, sizeof(ip6Header), &ip6Header);
mAddMeshHeader = false;
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
if (ip6Header.GetDestination().IsLinkLocal())
{
GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest);
}
else
{
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = aChild.mValid.mRloc16;
}
break;
case Message::kType6lowpan:
{
Lowpan::MeshHeader meshHeader;
IgnoreReturnValue(meshHeader.Init(*message));
mAddMeshHeader = true;
mMeshDest = meshHeader.GetDestination();
mMeshSource = meshHeader.GetSource();
mMacSource.mLength = sizeof(mMacSource.mShortAddress);
mMacSource.mShortAddress = mNetif.GetMac().GetShortAddress();
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = meshHeader.GetDestination();
break;
}
default:
assert(false);
break;
}
exit:
return message;
}
ThreadError MeshForwarder::UpdateMeshRoute(Message &aMessage)
{
ThreadError error = kThreadError_None;
Lowpan::MeshHeader meshHeader;
Neighbor *neighbor;
uint16_t nextHop;
IgnoreReturnValue(meshHeader.Init(aMessage));
nextHop = mNetif.GetMle().GetNextHop(meshHeader.GetDestination());
if (nextHop != Mac::kShortAddrInvalid)
{
neighbor = mNetif.GetMle().GetNeighbor(nextHop);
}
else
{
neighbor = mNetif.GetMle().GetNeighbor(meshHeader.GetDestination());
}
if (neighbor == NULL)
{
ExitNow(error = kThreadError_Drop);
}
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = neighbor->mValid.mRloc16;
mMacSource.mLength = sizeof(mMacSource.mShortAddress);
mMacSource.mShortAddress = mNetif.GetMac().GetShortAddress();
mAddMeshHeader = true;
mMeshDest = meshHeader.GetDestination();
mMeshSource = meshHeader.GetSource();
exit:
return error;
}
ThreadError MeshForwarder::UpdateIp6Route(Message &aMessage)
{
ThreadError error = kThreadError_None;
Ip6::Header ip6Header;
uint16_t rloc16;
uint16_t aloc16;
Neighbor *neighbor;
mAddMeshHeader = false;
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
switch (mNetif.GetMle().GetDeviceState())
{
case Mle::kDeviceStateDisabled:
case Mle::kDeviceStateDetached:
if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast())
{
GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest);
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
}
else
{
ExitNow(error = kThreadError_Drop);
}
break;
case Mle::kDeviceStateChild:
if (aMessage.IsLinkSecurityEnabled())
{
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
if (ip6Header.GetDestination().IsLinkLocalMulticast())
{
mMacDest.mShortAddress = Mac::kShortAddrBroadcast;
}
else
{
mMacDest.mShortAddress = mNetif.GetMle().GetNextHop(Mac::kShortAddrBroadcast);
}
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
}
else if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast())
{
GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest);
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
}
else
{
ExitNow(error = kThreadError_Drop);
}
break;
case Mle::kDeviceStateRouter:
case Mle::kDeviceStateLeader:
if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsMulticast())
{
GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest);
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
}
else
{
if (mNetif.GetMle().IsRoutingLocator(ip6Header.GetDestination()))
{
rloc16 = HostSwap16(ip6Header.GetDestination().mFields.m16[7]);
VerifyOrExit(mNetif.GetMle().IsRouterIdValid(mNetif.GetMle().GetRouterId(rloc16)),
error = kThreadError_Drop);
mMeshDest = rloc16;
}
else if (mNetif.GetMle().IsAnycastLocator(ip6Header.GetDestination()))
{
// only support Leader ALOC for now
aloc16 = HostSwap16(ip6Header.GetDestination().mFields.m16[7]);
if (aloc16 == Mle::kAloc16Leader)
{
mMeshDest = mNetif.GetMle().GetRloc16(mNetif.GetMle().GetLeaderId());
}
#if OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT
else if ((aloc16 & Mle::kAloc16DhcpAgentMask) != 0)
{
uint16_t agentRloc16;
uint8_t routerId;
VerifyOrExit((mNetif.GetNetworkDataLeader().GetRlocByContextId(
static_cast<uint8_t>(aloc16 & Mle::kAloc16DhcpAgentMask),
agentRloc16) == kThreadError_None),
error = kThreadError_Drop);
routerId = mNetif.GetMle().GetRouterId(agentRloc16);
// if agent is active router or the child of the device
if ((mNetif.GetMle().IsActiveRouter(agentRloc16)) ||
(mNetif.GetMle().GetRloc16(routerId) == mNetif.GetMle().GetRloc16()))
{
mMeshDest = agentRloc16;
}
else
{
// use the parent of the ED Agent as Dest
mMeshDest = mNetif.GetMle().GetRloc16(routerId);
}
}
#endif // OPENTHREAD_ENABLE_DHCP6_SERVER || OPENTHREAD_ENABLE_DHCP6_CLIENT
else
{
// TODO: support ALOC for DHCPv6 Agent, Service, Commissioner, Neighbor Discovery Agent
ExitNow(error = kThreadError_Drop);
}
}
else if ((neighbor = mNetif.GetMle().GetNeighbor(ip6Header.GetDestination())) != NULL)
{
mMeshDest = neighbor->mValid.mRloc16;
}
else if (mNetif.GetNetworkDataLeader().IsOnMesh(ip6Header.GetDestination()))
{
SuccessOrExit(error = mNetif.GetAddressResolver().Resolve(ip6Header.GetDestination(), mMeshDest));
}
else
{
mNetif.GetNetworkDataLeader().RouteLookup(
ip6Header.GetSource(),
ip6Header.GetDestination(),
NULL,
&mMeshDest
);
}
VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = kThreadError_Drop);
if (mNetif.GetMle().GetNeighbor(mMeshDest) != NULL)
{
// destination is neighbor
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = mMeshDest;
GetMacSourceAddress(ip6Header.GetSource(), mMacSource);
}
else
{
// destination is not neighbor
mMeshSource = mNetif.GetMac().GetShortAddress();
SuccessOrExit(error = mNetif.GetMle().CheckReachability(mMeshSource, mMeshDest, ip6Header));
mMacDest.mLength = sizeof(mMacDest.mShortAddress);
mMacDest.mShortAddress = mNetif.GetMle().GetNextHop(mMeshDest);
mMacSource.mLength = sizeof(mMacSource.mShortAddress);
mMacSource.mShortAddress = mMeshSource;
mAddMeshHeader = true;
}
}
break;
}
exit:
return error;
}
bool MeshForwarder::GetRxOnWhenIdle()
{
return mNetif.GetMac().GetRxOnWhenIdle();
}
void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle)
{
mNetif.GetMac().SetRxOnWhenIdle(aRxOnWhenIdle);
if (aRxOnWhenIdle)
{
mPollTimer.Stop();
}
else
{
mPollTimer.Start(mPollPeriod);
}
}
void MeshForwarder::SetAssignPollPeriod(uint32_t aPeriod)
{
mAssignPollPeriod = aPeriod;
if (mPollTimer.IsRunning() && ((mNetif.GetMle().GetDeviceMode() & Mle::ModeTlv::kModeFFD) == 0))
{
SetPollPeriod(mAssignPollPeriod);
}
}
uint32_t MeshForwarder::GetAssignPollPeriod()
{
return mAssignPollPeriod;
}
void MeshForwarder::SetPollPeriod(uint32_t aPeriod)
{
if (mPollPeriod != aPeriod)
{
if (mAssignPollPeriod != 0 && aPeriod != (OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD))
{
mPollPeriod = mAssignPollPeriod;
}
else
{
mPollPeriod = aPeriod;
}
mPollTimer.Start(mPollPeriod);
}
}
uint32_t MeshForwarder::GetPollPeriod()
{
return mPollPeriod;
}
void MeshForwarder::HandlePollTimer(void *aContext)
{
static_cast<MeshForwarder *>(aContext)->HandlePollTimer();
}
void MeshForwarder::HandlePollTimer()
{
ThreadError error;
error = SendMacDataRequest();
switch (error)
{
case kThreadError_None:
break;
case kThreadError_InvalidState:
// The poll timer should have been stopped. Hitting
// this might indicate a logic error.
otLogWarnMac("Poll timer fired while RxOnWhenIdle set!");
break;
case kThreadError_NoBufs:
// Failed to send DataRequest due to a lack of buffers.
// Try again following a brief pause to free buffers.
mPollTimer.Start(kDataRequstRetryDelay);
break;
case kThreadError_Already:
// This is perhaps a sign of
// bad behavior, as it suggests that mPollPeriod was not long
// enough for the previously scheduled DataRequest to get out of
// the sendQueue.
otLogDebgMac("Poll timer fired with DataRequest in SendQueue.");
// Intentional fall-thru
default:
// Restart for any other error which might originate from SendMessage().
mPollTimer.Start(mPollPeriod);
break;
}
}
ThreadError MeshForwarder::SendMacDataRequest(void)
{
ThreadError error;
Message *message;
// only send MAC Data Requests in rx-off-when-idle mode
VerifyOrExit(!mNetif.GetMac().GetRxOnWhenIdle(), error = kThreadError_InvalidState);
// only enqueue one MAC Data Request at a time
for (message = mSendQueue.GetHead(); message; message = message->GetNext())
{
VerifyOrExit(message->GetType() != Message::kTypeMacDataPoll, error = kThreadError_Already);
}
// enqueue a MAC Data Request message
message = mNetif.GetIp6().mMessagePool.New(Message::kTypeMacDataPoll, 0);
VerifyOrExit(message != NULL, error = kThreadError_NoBufs);
error = SendMessage(*message);
if (error == kThreadError_None)
{
otLogInfoMac("Sent poll");
// restart the polling timer
mPollTimer.Start(mPollPeriod);
}
else
{
message->Free();
message = NULL;
}
exit:
return error;
}
ThreadError MeshForwarder::GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr)
{
aMacAddr.mLength = sizeof(aMacAddr.mExtAddress);
aMacAddr.mExtAddress.Set(aIp6Addr);
if (memcmp(&aMacAddr.mExtAddress, mNetif.GetMac().GetExtAddress(), sizeof(aMacAddr.mExtAddress)) != 0)
{
aMacAddr.mLength = sizeof(aMacAddr.mShortAddress);
aMacAddr.mShortAddress = mNetif.GetMac().GetShortAddress();
}
return kThreadError_None;
}
ThreadError MeshForwarder::GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr)
{
if (aIp6Addr.IsMulticast())
{
aMacAddr.mLength = sizeof(aMacAddr.mShortAddress);
aMacAddr.mShortAddress = 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.mLength = sizeof(aMacAddr.mShortAddress);
aMacAddr.mShortAddress = HostSwap16(aIp6Addr.mFields.m16[7]);
}
else if (mNetif.GetMle().IsRoutingLocator(aIp6Addr))
{
aMacAddr.mLength = sizeof(aMacAddr.mShortAddress);
aMacAddr.mShortAddress = HostSwap16(aIp6Addr.mFields.m16[7]);
}
else
{
aMacAddr.mLength = sizeof(aMacAddr.mExtAddress);
aMacAddr.mExtAddress.Set(aIp6Addr);
}
return kThreadError_None;
}
ThreadError MeshForwarder::HandleFrameRequest(void *aContext, Mac::Frame &aFrame)
{
return static_cast<MeshForwarder *>(aContext)->HandleFrameRequest(aFrame);
}
ThreadError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
{
ThreadError error = kThreadError_None;
Mac::Address macDest;
Child *child = NULL;
VerifyOrExit(mEnabled, error = kThreadError_Abort);
mSendBusy = true;
if (mSendMessage == NULL)
{
SendEmptyFrame(aFrame);
ExitNow();
}
switch (mSendMessage->GetType())
{
case Message::kTypeIp6:
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
if (!mScanning)
{
mScanChannel = kPhyMinChannel;
mRestoreChannel = mNetif.GetMac().GetChannel();
mRestorePanId = mNetif.GetMac().GetPanId();
mScanning = true;
}
while ((mScanChannels & 1) == 0)
{
mScanChannels >>= 1;
mScanChannel++;
}
mNetif.GetMac().SetChannel(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 &&
mNetif.GetMac().GetPanId() == Mac::kPanIdBroadcast)
{
uint16_t panid;
do
{
panid = static_cast<uint16_t>(otPlatRandomGet());
}
while (panid == Mac::kPanIdBroadcast);
mNetif.GetMac().SetPanId(panid);
}
}
if (SendFragment(*mSendMessage, aFrame) == kThreadError_NotCapable)
{
SendFragment(*mSendMessage, aFrame);
}
assert(aFrame.GetLength() != 7);
break;
case Message::kType6lowpan:
SendMesh(*mSendMessage, aFrame);
break;
case Message::kTypeMacDataPoll:
SendPoll(*mSendMessage, aFrame);
break;
}
// set FramePending if there are more queued messages for the child
aFrame.GetDstAddr(macDest);
if (((child = mNetif.GetMle().GetChild(macDest)) != NULL)
&& ((child->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
&& (child->mQueuedIndirectMessageCnt > 1))
{
aFrame.SetFramePending(true);
}
exit:
return error;
}
ThreadError MeshForwarder::SendPoll(Message &aMessage, Mac::Frame &aFrame)
{
ThreadError error = kThreadError_None;
Mac::Address macSource;
uint16_t fcf;
Neighbor *neighbor;
// only send MAC Data Requests in rx-off-when-idle mode
VerifyOrExit(!mNetif.GetMac().GetRxOnWhenIdle(), error = kThreadError_InvalidState);
macSource.mShortAddress = mNetif.GetMac().GetShortAddress();
if (macSource.mShortAddress != Mac::kShortAddrInvalid)
{
macSource.mLength = sizeof(macSource.mShortAddress);
}
else
{
macSource.mLength = sizeof(macSource.mExtAddress);
memcpy(&macSource.mExtAddress, mNetif.GetMac().GetExtAddress(), sizeof(macSource.mExtAddress));
}
// initialize MAC header
fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfFrameVersion2006;
if (macSource.mLength == sizeof(Mac::ShortAddress))
{
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(mNetif.GetMac().GetPanId());
neighbor = mNetif.GetMle().GetParent();
assert(neighbor != NULL);
if (macSource.mLength == 2)
{
aFrame.SetDstAddr(neighbor->mValid.mRloc16);
aFrame.SetSrcAddr(macSource.mShortAddress);
}
else
{
aFrame.SetDstAddr(neighbor->mMacAddr);
aFrame.SetSrcAddr(macSource.mExtAddress);
}
aFrame.SetCommandId(Mac::Frame::kMacCmdDataRequest);
mMessageNextOffset = aMessage.GetLength();
exit:
return error;
}
ThreadError MeshForwarder::SendMesh(Message &aMessage, Mac::Frame &aFrame)
{
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(mNetif.GetMac().GetPanId());
aFrame.SetDstAddr(mMacDest.mShortAddress);
aFrame.SetSrcAddr(mMacSource.mShortAddress);
// 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 kThreadError_None;
}
ThreadError MeshForwarder::SendFragment(Message &aMessage, Mac::Frame &aFrame)
{
Mac::Address meshDest, meshSource;
uint16_t fcf;
Lowpan::FragmentHeader *fragmentHeader;
Lowpan::MeshHeader meshHeader;
uint8_t *payload;
uint8_t headerLength;
uint8_t hopsLeft;
uint16_t payloadLength;
int hcLength;
uint16_t fragmentLength;
uint16_t dstpan;
uint8_t secCtl = Mac::Frame::kSecNone;
ThreadError error = kThreadError_None;
if (mAddMeshHeader)
{
meshSource.mLength = sizeof(meshSource.mShortAddress);
meshSource.mShortAddress = mMeshSource;
meshDest.mLength = sizeof(meshDest.mShortAddress);
meshDest.mShortAddress = mMeshDest;
}
else
{
meshDest = mMacDest;
meshSource = mMacSource;
}
// initialize MAC header
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006;
fcf |= (mMacDest.mLength == 2) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (mMacSource.mLength == 2) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
// all unicast frames request ACK
if (mMacDest.mLength == 8 || mMacDest.mShortAddress != Mac::kShortAddrBroadcast)
{
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 = mNetif.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 == mNetif.GetMac().GetPanId())
{
fcf |= Mac::Frame::kFcfPanidCompression;
}
aFrame.InitMacHeader(fcf, secCtl);
aFrame.SetDstPanId(dstpan);
aFrame.SetSrcPanId(mNetif.GetMac().GetPanId());
if (mMacDest.mLength == 2)
{
aFrame.SetDstAddr(mMacDest.mShortAddress);
}
else
{
aFrame.SetDstAddr(mMacDest.mExtAddress);
}
if (mMacSource.mLength == 2)
{
aFrame.SetSrcAddr(mMacSource.mShortAddress);
}
else
{
aFrame.SetSrcAddr(mMacSource.mExtAddress);
}
payload = aFrame.GetPayload();
headerLength = 0;
// initialize Mesh header
if (mAddMeshHeader)
{
if (mNetif.GetMle().GetDeviceState() == Mle::kDeviceStateChild)
{
// 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 = mNetif.GetMle().GetRouteCost(mMeshDest);
if (hopsLeft != Mle::kMaxRouteCost)
{
hopsLeft += mNetif.GetMle().GetLinkCost(
mNetif.GetMle().GetRouterId(mNetif.GetMle().GetNextHop(mMeshDest)));
}
else
{
// In case there is no route to the destination router (only link).
hopsLeft = mNetif.GetMle().GetLinkCost(mNetif.GetMle().GetRouterId(mMeshDest));
}
}
// The hopsLft field MUST be incremented by one if the destination RLOC16
// is not that of an active Router.
if (!mNetif.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();
}
// copy IPv6 Header
if (aMessage.GetOffset() == 0)
{
hcLength = mNetif.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.SetLinkSecurityEnabled(true);
aMessage.SetOffset(0);
ExitNow(error = kThreadError_NotCapable);
}
// 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);
}
exit:
return error;
}
ThreadError MeshForwarder::SendEmptyFrame(Mac::Frame &aFrame)
{
uint16_t fcf;
uint8_t secCtl;
Mac::Address macSource;
macSource.mShortAddress = mNetif.GetMac().GetShortAddress();
if (macSource.mShortAddress != Mac::kShortAddrInvalid)
{
macSource.mLength = sizeof(macSource.mShortAddress);
}
else
{
macSource.mLength = sizeof(macSource.mExtAddress);
memcpy(&macSource.mExtAddress, mNetif.GetMac().GetExtAddress(), sizeof(macSource.mExtAddress));
}
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006;
fcf |= (mMacDest.mLength == 2) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (macSource.mLength == 2) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
// Not requesting acknowledgment for null/empty frame.
fcf |= Mac::Frame::kFcfSecurityEnabled;
secCtl = Mac::Frame::kKeyIdMode1;
secCtl |= Mac::Frame::kSecEncMic32;
fcf |= Mac::Frame::kFcfPanidCompression;
aFrame.InitMacHeader(fcf, secCtl);
aFrame.SetDstPanId(mNetif.GetMac().GetPanId());
aFrame.SetSrcPanId(mNetif.GetMac().GetPanId());
if (mMacDest.mLength == 2)
{
aFrame.SetDstAddr(mMacDest.mShortAddress);
}
else
{
aFrame.SetDstAddr(mMacDest.mExtAddress);
}
if (macSource.mLength == 2)
{
aFrame.SetSrcAddr(macSource.mShortAddress);
}
else
{
aFrame.SetSrcAddr(macSource.mExtAddress);
}
aFrame.SetPayloadLength(0);
aFrame.SetFramePending(false);
return kThreadError_None;
}
void MeshForwarder::HandleSentFrame(void *aContext, Mac::Frame &aFrame, ThreadError aError)
{
static_cast<MeshForwarder *>(aContext)->HandleSentFrame(aFrame, aError);
}
void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError)
{
Mac::Address macDest;
Child *child;
Neighbor *neighbor;
mSendBusy = false;
VerifyOrExit(mEnabled, ;);
if (mSendMessage != NULL)
{
mSendMessage->SetOffset(mMessageNextOffset);
}
aFrame.GetDstAddr(macDest);
if ((neighbor = mNetif.GetMle().GetNeighbor(macDest)) != NULL)
{
switch (aError)
{
case kThreadError_None:
if (aFrame.GetAckRequest())
{
neighbor->mLinkFailures = 0;
}
break;
case kThreadError_ChannelAccessFailure:
case kThreadError_Abort:
break;
case kThreadError_NoAck:
neighbor->mLinkFailures++;
if (mNetif.GetMle().IsActiveRouter(neighbor->mValid.mRloc16))
{
if (neighbor->mLinkFailures >= Mle::kFailedRouterTransmissions)
{
mNetif.GetMle().RemoveNeighbor(*neighbor);
}
}
break;
default:
assert(false);
break;
}
}
if ((child = mNetif.GetMle().GetChild(macDest)) != NULL)
{
child->mDataRequest = false;
VerifyOrExit(mSendMessage != NULL, ;);
if (mMessageNextOffset < mSendMessage->GetLength())
{
child->mFragmentOffset = mMessageNextOffset;
}
else
{
child->mFragmentOffset = 0;
mSendMessage->ClearChildMask(mNetif.GetMle().GetChildIndex(*child));
if ((child->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
{
child->mQueuedIndirectMessageCnt--;
otLogDebgMac("Sent to child (0x%x), still queued message (%d)",
child->mValid.mRloc16, child->mQueuedIndirectMessageCnt);
if (child->mQueuedIndirectMessageCnt == 0)
{
ClearSrcMatchEntry(*child);
}
}
}
}
VerifyOrExit(mSendMessage != NULL, ;);
if (mSendMessage->GetDirectTransmission())
{
if (mMessageNextOffset < mSendMessage->GetLength())
{
mSendMessage->SetOffset(mMessageNextOffset);
}
else
{
mSendMessage->ClearDirectTransmission();
mSendMessage->SetOffset(0);
}
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
mSendBusy = true;
mDiscoverTimer.Start(mScanDuration);
ExitNow();
}
}
if (mSendMessage->GetType() == Message::kTypeMacDataPoll)
{
neighbor = mNetif.GetMle().GetParent();
if (neighbor->mState == Neighbor::kStateInvalid)
{
mPollTimer.Stop();
mNetif.GetMle().BecomeDetached();
}
}
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, uint16_t aScanDuration)
{
mScanChannels = (aScanChannels == 0) ? static_cast<uint32_t>(Mac::kScanChannelsAll) : aScanChannels;
mScanDuration = (aScanDuration == 0) ? static_cast<uint16_t>(Mac::kScanDurationDefault) : aScanDuration;
}
void MeshForwarder::HandleDiscoverTimer(void *aContext)
{
MeshForwarder *obj = static_cast<MeshForwarder *>(aContext);
obj->HandleDiscoverTimer();
}
void MeshForwarder::HandleDiscoverTimer(void)
{
do
{
mScanChannels >>= 1;
mScanChannel++;
if (mScanChannel > kPhyMaxChannel)
{
mSendQueue.Dequeue(*mSendMessage);
mSendMessage->Free();
mSendMessage = NULL;
mNetif.GetMac().SetChannel(mRestoreChannel);
mNetif.GetMac().SetPanId(mRestorePanId);
mScanning = false;
mNetif.GetMle().HandleDiscoverComplete();
ExitNow();
}
}
while ((mScanChannels & 1) == 0);
mSendMessage->SetDirectTransmission();
exit:
mSendBusy = false;
mScheduleTransmissionTask.Post();
}
void MeshForwarder::HandleReceivedFrame(void *aContext, Mac::Frame &aFrame)
{
static_cast<MeshForwarder *>(aContext)->HandleReceivedFrame(aFrame);
}
void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame)
{
ThreadMessageInfo messageInfo;
Mac::Address macDest;
Mac::Address macSource;
uint8_t *payload;
uint8_t payloadLength;
uint8_t commandId;
Child *child = NULL;
ThreadError error = kThreadError_None;
if (!mEnabled)
{
ExitNow(error = kThreadError_InvalidState);
}
SuccessOrExit(error = aFrame.GetSrcAddr(macSource));
SuccessOrExit(aFrame.GetDstAddr(macDest));
if ((child = mNetif.GetMle().GetChild(macSource)) != NULL)
{
if (((child->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0) &&
macSource.mLength == sizeof(otShortAddress))
{
child->mAddSrcMatchEntryShort = true;
}
}
aFrame.GetSrcPanId(messageInfo.mPanId);
messageInfo.mChannel = aFrame.GetChannel();
messageInfo.mRss = aFrame.GetPower();
messageInfo.mLqi = aFrame.GetLqi();
messageInfo.mLinkSecurity = aFrame.GetSecurityEnabled();
payload = aFrame.GetPayload();
payloadLength = aFrame.GetPayloadLength();
if (mPollTimer.IsRunning() && aFrame.GetFramePending())
{
// add delay to avoid packet loss due to possible switch senarios between transmit/receive status
mPollTimer.Start(OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD);
}
switch (aFrame.GetType())
{
case Mac::Frame::kFcfFrameData:
if (payloadLength >= sizeof(Lowpan::MeshHeader) &&
reinterpret_cast<Lowpan::MeshHeader *>(payload)->IsMeshHeader())
{
HandleMesh(payload, payloadLength, macSource, messageInfo);
}
else if (payloadLength >= sizeof(Lowpan::FragmentHeader) &&
reinterpret_cast<Lowpan::FragmentHeader *>(payload)->IsFragmentHeader())
{
HandleFragment(payload, payloadLength, macSource, macDest, messageInfo);
}
else if (payloadLength >= 1 &&
Lowpan::Lowpan::IsLowpanHc(payload))
{
HandleLowpanHC(payload, payloadLength, macSource, macDest, messageInfo);
}
break;
case Mac::Frame::kFcfFrameMacCmd:
aFrame.GetCommandId(commandId);
if (commandId == Mac::Frame::kMacCmdDataRequest)
{
HandleDataRequest(macSource, messageInfo);
}
break;
}
exit:
if (error != kThreadError_None)
{
otLogDebgMacErr(error, "Dropping received frame");
}
}
void MeshForwarder::HandleMesh(uint8_t *aFrame, uint8_t aFrameLength, const Mac::Address &aMacSource,
const ThreadMessageInfo &aMessageInfo)
{
ThreadError error = kThreadError_None;
Message *message = NULL;
Mac::Address meshDest;
Mac::Address meshSource;
Lowpan::MeshHeader meshHeader;
// Check the mesh header
VerifyOrExit(meshHeader.Init(aFrame, aFrameLength) == kThreadError_None, error = kThreadError_Drop);
// Security Check: only process Mesh Header frames that had security enabled.
VerifyOrExit(aMessageInfo.mLinkSecurity && meshHeader.IsValid(), error = kThreadError_Security);
meshSource.mLength = sizeof(meshSource.mShortAddress);
meshSource.mShortAddress = meshHeader.GetSource();
meshDest.mLength = sizeof(meshDest.mShortAddress);
meshDest.mShortAddress = meshHeader.GetDestination();
if (meshDest.mShortAddress == mNetif.GetMac().GetShortAddress())
{
aFrame += meshHeader.GetHeaderLength();
aFrameLength -= meshHeader.GetHeaderLength();
if (reinterpret_cast<Lowpan::FragmentHeader *>(aFrame)->IsFragmentHeader())
{
HandleFragment(aFrame, aFrameLength, meshSource, meshDest, aMessageInfo);
}
else if (Lowpan::Lowpan::IsLowpanHc(aFrame))
{
HandleLowpanHC(aFrame, aFrameLength, meshSource, meshDest, aMessageInfo);
}
else
{
ExitNow(error = kThreadError_Parse);
}
}
else if (meshHeader.GetHopsLeft() > 0)
{
mNetif.GetMle().ResolveRoutingLoops(aMacSource.mShortAddress, meshDest.mShortAddress);
SuccessOrExit(error = CheckReachability(aFrame, aFrameLength, meshSource, meshDest));
meshHeader.SetHopsLeft(meshHeader.GetHopsLeft() - 1);
meshHeader.AppendTo(aFrame);
VerifyOrExit((message = mNetif.GetIp6().mMessagePool.New(Message::kType6lowpan, 0)) != NULL,
error = kThreadError_NoBufs);
SuccessOrExit(error = message->SetLength(aFrameLength));
message->Write(0, aFrameLength, aFrame);
message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity);
message->SetPanId(aMessageInfo.mPanId);
SendMessage(*message);
}
exit:
if (error != kThreadError_None)
{
otLogDebgMacErr(error, "Dropping received mesh frame");
if (message != NULL)
{
message->Free();
}
}
}
ThreadError MeshForwarder::CheckReachability(uint8_t *aFrame, uint8_t aFrameLength,
const Mac::Address &aMeshSource, const Mac::Address &aMeshDest)
{
ThreadError error = kThreadError_None;
Ip6::Header ip6Header;
Lowpan::MeshHeader meshHeader;
VerifyOrExit(meshHeader.Init(aFrame, aFrameLength) == kThreadError_None, error = kThreadError_Drop);
// skip mesh header
aFrame += meshHeader.GetHeaderLength();
aFrameLength -= meshHeader.GetHeaderLength();
// skip fragment header
if (aFrameLength >= 1 &&
reinterpret_cast<Lowpan::FragmentHeader *>(aFrame)->IsFragmentHeader())
{
VerifyOrExit(sizeof(Lowpan::FragmentHeader) <= aFrameLength, error = kThreadError_Drop);
VerifyOrExit(reinterpret_cast<Lowpan::FragmentHeader *>(aFrame)->GetDatagramOffset() == 0,);
aFrame += reinterpret_cast<Lowpan::FragmentHeader *>(aFrame)->GetHeaderLength();
aFrameLength -= reinterpret_cast<Lowpan::FragmentHeader *>(aFrame)->GetHeaderLength();
}
// only process IPv6 packets
VerifyOrExit(aFrameLength >= 1 && Lowpan::Lowpan::IsLowpanHc(aFrame),);
VerifyOrExit(mNetif.GetLowpan().DecompressBaseHeader(ip6Header, aMeshSource, aMeshDest, aFrame, aFrameLength) > 0,
error = kThreadError_Drop);
error = mNetif.GetMle().CheckReachability(aMeshSource.mShortAddress, aMeshDest.mShortAddress, ip6Header);
exit:
return error;
}
void MeshForwarder::HandleFragment(uint8_t *aFrame, uint8_t aFrameLength,
const Mac::Address &aMacSource, const Mac::Address &aMacDest,
const ThreadMessageInfo &aMessageInfo)
{
ThreadError error = kThreadError_None;
Lowpan::FragmentHeader *fragmentHeader = reinterpret_cast<Lowpan::FragmentHeader *>(aFrame);
uint16_t datagramLength = fragmentHeader->GetDatagramSize();
uint16_t datagramTag = fragmentHeader->GetDatagramTag();
Message *message = NULL;
int headerLength;
if (fragmentHeader->GetDatagramOffset() == 0)
{
aFrame += fragmentHeader->GetHeaderLength();
aFrameLength -= fragmentHeader->GetHeaderLength();
VerifyOrExit((message = mNetif.GetIp6().mMessagePool.New(Message::kTypeIp6, 0)) != NULL,
error = kThreadError_NoBufs);
message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity);
message->SetPanId(aMessageInfo.mPanId);
headerLength = mNetif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength,
datagramLength);
VerifyOrExit(headerLength > 0, error = kThreadError_Parse);
aFrame += headerLength;
aFrameLength -= static_cast<uint8_t>(headerLength);
VerifyOrExit(datagramLength >= message->GetOffset() + aFrameLength, error = kThreadError_Parse);
SuccessOrExit(error = message->SetLength(datagramLength));
message->SetDatagramTag(datagramTag);
message->SetTimeout(kReassemblyTimeout);
// copy Fragment
message->Write(message->GetOffset(), aFrameLength, aFrame);
message->MoveOffset(aFrameLength);
// Security Check
VerifyOrExit(mNetif.GetIp6Filter().Accept(*message), error = kThreadError_Drop);
mReassemblyList.Enqueue(*message);
if (!mReassemblyTimer.IsRunning())
{
mReassemblyTimer.Start(kStateUpdatePeriod);
}
}
else
{
aFrame += fragmentHeader->GetHeaderLength();
aFrameLength -= fragmentHeader->GetHeaderLength();
for (message = mReassemblyList.GetHead(); message; message = message->GetNext())
{
// Security Check: only consider reassembly buffers that had the same Security Enabled setting.
if (message->GetLength() == datagramLength &&
message->GetDatagramTag() == datagramTag &&
message->GetOffset() == fragmentHeader->GetDatagramOffset() &&
message->IsLinkSecurityEnabled() == aMessageInfo.mLinkSecurity)
{
break;
}
}
VerifyOrExit(message != NULL, error = kThreadError_Drop);
// copy Fragment
message->Write(message->GetOffset(), aFrameLength, aFrame);
message->MoveOffset(aFrameLength);
}
exit:
if (error == kThreadError_None)
{
if (message->GetOffset() >= message->GetLength())
{
mReassemblyList.Dequeue(*message);
HandleDatagram(*message, aMessageInfo);
}
}
else
{
otLogDebgMacErr(error, "Dropping received fragment");
if (message != NULL)
{
message->Free();
}
}
}
void MeshForwarder::HandleReassemblyTimer(void *aContext)
{
static_cast<MeshForwarder *>(aContext)->HandleReassemblyTimer();
}
void MeshForwarder::HandleReassemblyTimer()
{
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);
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 ThreadMessageInfo &aMessageInfo)
{
ThreadError error = kThreadError_None;
Message *message;
int headerLength;
VerifyOrExit((message = mNetif.GetIp6().mMessagePool.New(Message::kTypeIp6, 0)) != NULL,
error = kThreadError_NoBufs);
message->SetLinkSecurityEnabled(aMessageInfo.mLinkSecurity);
message->SetPanId(aMessageInfo.mPanId);
headerLength = mNetif.GetLowpan().Decompress(*message, aMacSource, aMacDest, aFrame, aFrameLength, 0);
VerifyOrExit(headerLength > 0, error = kThreadError_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(mNetif.GetIp6Filter().Accept(*message), error = kThreadError_Drop);
exit:
if (error == kThreadError_None)
{
HandleDatagram(*message, aMessageInfo);
}
else
{
otLogDebgMacErr(error, "Dropping received lowpan HC");
if (message != NULL)
{
message->Free();
}
}
}
ThreadError MeshForwarder::HandleDatagram(Message &aMessage, const ThreadMessageInfo &aMessageInfo)
{
return mNetif.GetIp6().HandleDatagram(aMessage, &mNetif, mNetif.GetInterfaceId(), &aMessageInfo, false);
}
void MeshForwarder::UpdateFramePending()
{
}
void MeshForwarder::HandleDataRequest(const Mac::Address &aMacSource, const ThreadMessageInfo &aMessageInfo)
{
Child *child;
// Security Check: only process secure Data Poll frames.
VerifyOrExit(aMessageInfo.mLinkSecurity, ;);
VerifyOrExit(mNetif.GetMle().GetDeviceState() != Mle::kDeviceStateDetached, ;);
VerifyOrExit((child = mNetif.GetMle().GetChild(aMacSource)) != NULL, ;);
child->mLastHeard = Timer::GetNow();
child->mLinkFailures = 0;
if (!mSrcMatchEnabled || child->mQueuedIndirectMessageCnt > 0)
{
child->mDataRequest = true;
}
mScheduleTransmissionTask.Post();
exit:
{}
}
} // namespace Thread