blob: a33f84f3399652c9f2738466e4e382033618fdf3 [file]
/*
* 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 the subset of IEEE 802.15.4 primitives required for Thread.
*/
#define WPP_NAME "mac.tmh"
#include "mac.hpp"
#include <stdio.h>
#include "utils/wrap_string.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/logging.hpp"
#include "common/owner-locator.hpp"
#include "common/random.hpp"
#include "crypto/aes_ccm.hpp"
#include "crypto/sha256.hpp"
#include "mac/mac_frame.hpp"
#include "thread/link_quality.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
using ot::Encoding::BigEndian::HostSwap64;
namespace ot {
namespace Mac {
static const uint8_t sMode2Key[] = {0x78, 0x58, 0x16, 0x86, 0xfd, 0xb4, 0x58, 0x0f,
0xb0, 0x92, 0x54, 0x6a, 0xec, 0xbd, 0x15, 0x66};
static const otExtAddress sMode2ExtAddress = {
{0x35, 0x06, 0xfe, 0xb8, 0x23, 0xd4, 0x87, 0x12},
};
static const otExtendedPanId sExtendedPanidInit = {
{0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe},
};
static const char sNetworkNameInit[] = "OpenThread";
Mac::Mac(Instance &aInstance)
: InstanceLocator(aInstance)
, mEnabled(true)
, mPendingActiveScan(false)
, mPendingEnergyScan(false)
, mPendingTransmitBeacon(false)
, mPendingTransmitData(false)
, mPendingTransmitOobFrame(false)
, mPendingWaitingForData(false)
, mRxOnWhenIdle(false)
, mBeaconsEnabled(false)
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
, mShouldDelaySleep(false)
, mDelayingSleep(false)
#endif
, mOperation(kOperationIdle)
, mBeaconSequence(Random::GetUint8())
, mDataSequence(Random::GetUint8())
, mBroadcastTransmitCount(0)
, mPanId(kPanIdBroadcast)
, mPanChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
, mRadioChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
, mRadioChannelAcquisitionId(0)
, mSupportedChannelMask(OT_RADIO_SUPPORTED_CHANNELS)
, mScanChannel(OT_RADIO_CHANNEL_MIN)
, mScanDuration(0)
, mScanChannelMask()
, mActiveScanHandler(NULL) /* Initialize `mActiveScanHandler` and `mEnergyScanHandler` union */
, mSubMac(aInstance, *this)
, mOperationTask(aInstance, &Mac::HandleOperationTask, this)
, mTimer(aInstance, &Mac::HandleTimer, this)
, mOobFrame(NULL)
, mKeyIdMode2FrameCounter(0)
, mCcaSampleCount(0)
#if OPENTHREAD_ENABLE_MAC_FILTER
, mFilter()
#endif
{
ExtAddress randomExtAddress;
randomExtAddress.GenerateRandom();
mCcaSuccessRateTracker.Reset();
memset(&mCounters, 0, sizeof(otMacCounters));
memset(&mNetworkName, 0, sizeof(otNetworkName));
memset(&mExtendedPanId, 0, sizeof(otExtendedPanId));
mSubMac.Enable();
SetExtendedPanId(sExtendedPanidInit);
SetNetworkName(sNetworkNameInit);
SetPanId(mPanId);
SetExtAddress(randomExtAddress);
SetShortAddress(GetShortAddress());
}
otError Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveScanHandler aHandler)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = OT_ERROR_BUSY);
mActiveScanHandler = aHandler;
if (aScanDuration == 0)
{
aScanDuration = kScanDurationDefault;
}
Scan(kOperationActiveScan, aScanChannels, aScanDuration);
exit:
return error;
}
otError Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergyScanHandler aHandler)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = OT_ERROR_BUSY);
mEnergyScanHandler = aHandler;
Scan(kOperationEnergyScan, aScanChannels, aScanDuration);
exit:
return error;
}
void Mac::Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanDuration)
{
mScanDuration = aScanDuration;
mScanChannel = ChannelMask::kChannelIteratorFirst;
if (aScanChannels == 0)
{
aScanChannels = OT_RADIO_SUPPORTED_CHANNELS;
}
mScanChannelMask.SetMask(aScanChannels);
mScanChannelMask.Intersect(mSupportedChannelMask);
StartOperation(aScanOperation);
}
bool Mac::IsActiveScanInProgress(void)
{
return (mOperation == kOperationActiveScan) || (mPendingActiveScan);
}
bool Mac::IsEnergyScanInProgress(void)
{
return (mOperation == kOperationEnergyScan) || (mPendingEnergyScan);
}
bool Mac::IsInTransmitState(void)
{
return (mOperation == kOperationTransmitData) || (mOperation == kOperationTransmitBeacon) ||
(mOperation == kOperationTransmitOutOfBandFrame);
}
otError Mac::ConvertBeaconToActiveScanResult(Frame *aBeaconFrame, otActiveScanResult &aResult)
{
otError error = OT_ERROR_NONE;
Address address;
Beacon * beacon = NULL;
BeaconPayload *beaconPayload = NULL;
uint8_t payloadLength;
memset(&aResult, 0, sizeof(otActiveScanResult));
VerifyOrExit(aBeaconFrame != NULL, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(aBeaconFrame->GetType() == Frame::kFcfFrameBeacon, error = OT_ERROR_PARSE);
SuccessOrExit(error = aBeaconFrame->GetSrcAddr(address));
VerifyOrExit(address.IsExtended(), error = OT_ERROR_PARSE);
aResult.mExtAddress = address.GetExtended();
aBeaconFrame->GetSrcPanId(aResult.mPanId);
aResult.mChannel = aBeaconFrame->GetChannel();
aResult.mRssi = aBeaconFrame->GetRssi();
aResult.mLqi = aBeaconFrame->GetLqi();
payloadLength = aBeaconFrame->GetPayloadLength();
beacon = reinterpret_cast<Beacon *>(aBeaconFrame->GetPayload());
beaconPayload = reinterpret_cast<BeaconPayload *>(beacon->GetPayload());
if ((payloadLength >= (sizeof(*beacon) + sizeof(*beaconPayload))) && beacon->IsValid() && beaconPayload->IsValid())
{
aResult.mVersion = beaconPayload->GetProtocolVersion();
aResult.mIsJoinable = beaconPayload->IsJoiningPermitted();
aResult.mIsNative = beaconPayload->IsNative();
memcpy(&aResult.mNetworkName, beaconPayload->GetNetworkName(), BeaconPayload::kNetworkNameSize);
memcpy(&aResult.mExtendedPanId, beaconPayload->GetExtendedPanId(), BeaconPayload::kExtPanIdSize);
}
LogBeacon("Received", *beaconPayload);
exit:
return error;
}
otError Mac::UpdateScanChannel(void)
{
otError error;
VerifyOrExit(mEnabled, error = OT_ERROR_ABORT);
error = mScanChannelMask.GetNextChannel(mScanChannel);
exit:
return error;
}
void Mac::PerformActiveScan(void)
{
if (UpdateScanChannel() == OT_ERROR_NONE)
{
// If there are more channels to scan, send the beacon request.
BeginTransmit();
}
else
{
mSubMac.SetPanId(mPanId);
FinishOperation();
mActiveScanHandler(GetInstance(), NULL);
PerformNextOperation();
}
}
void Mac::PerformEnergyScan(void)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = UpdateScanChannel());
if (mScanDuration == 0)
{
while (true)
{
mSubMac.Receive(mScanChannel);
ReportEnergyScanResult(mSubMac.GetRssi());
SuccessOrExit(error = UpdateScanChannel());
}
}
else
{
error = mSubMac.EnergyScan(mScanChannel, mScanDuration);
}
exit:
if (error != OT_ERROR_NONE)
{
FinishOperation();
mEnergyScanHandler(GetInstance(), NULL);
PerformNextOperation();
}
}
void Mac::ReportEnergyScanResult(int8_t aRssi)
{
if (aRssi != kInvalidRssiValue)
{
otEnergyScanResult result;
result.mChannel = mScanChannel;
result.mMaxRssi = aRssi;
mEnergyScanHandler(GetInstance(), &result);
}
}
void Mac::EnergyScanDone(int8_t aRssi)
{
ReportEnergyScanResult(aRssi);
PerformEnergyScan();
}
void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
{
VerifyOrExit(mRxOnWhenIdle != aRxOnWhenIdle);
mRxOnWhenIdle = aRxOnWhenIdle;
// If the new value for `mRxOnWhenIdle` is `true` (i.e., radio should
// remain in Rx while idle) we stop any ongoing or pending `WaitingForData`
// operation (since this operation only applies to sleepy devices).
if (mRxOnWhenIdle)
{
if (mPendingWaitingForData)
{
mTimer.Stop();
mPendingWaitingForData = false;
}
if (mOperation == kOperationWaitingForData)
{
mTimer.Stop();
FinishOperation();
mOperationTask.Post();
}
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
mDelayingSleep = false;
mShouldDelaySleep = false;
#endif
}
mSubMac.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous);
UpdateIdleMode();
exit:
return;
}
void Mac::SetShortAddress(ShortAddress aShortAddress)
{
mSubMac.SetShortAddress(aShortAddress);
}
otError Mac::SetPanChannel(uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mPanChannel != aChannel, GetNotifier().SignalIfFirst(OT_CHANGED_THREAD_CHANNEL));
mPanChannel = aChannel;
mCcaSuccessRateTracker.Reset();
GetNotifier().Signal(OT_CHANGED_THREAD_CHANNEL);
VerifyOrExit(!mRadioChannelAcquisitionId);
mRadioChannel = mPanChannel;
UpdateIdleMode();
exit:
return error;
}
otError Mac::SetRadioChannel(uint16_t aAcquisitionId, uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mRadioChannelAcquisitionId && aAcquisitionId == mRadioChannelAcquisitionId,
error = OT_ERROR_INVALID_STATE);
mRadioChannel = aChannel;
UpdateIdleMode();
exit:
return error;
}
otError Mac::AcquireRadioChannel(uint16_t *aAcquisitionId)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aAcquisitionId != NULL, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(!mRadioChannelAcquisitionId, error = OT_ERROR_INVALID_STATE);
mRadioChannelAcquisitionId = Random::GetUint16InRange(1, kMaxAcquisitionId);
*aAcquisitionId = mRadioChannelAcquisitionId;
exit:
return error;
}
otError Mac::ReleaseRadioChannel(void)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mRadioChannelAcquisitionId, error = OT_ERROR_INVALID_STATE);
mRadioChannelAcquisitionId = 0;
mRadioChannel = mPanChannel;
UpdateIdleMode();
exit:
return error;
}
void Mac::SetSupportedChannelMask(const ChannelMask &aMask)
{
ChannelMask newMask = aMask;
newMask.Intersect(OT_RADIO_SUPPORTED_CHANNELS);
VerifyOrExit(newMask != mSupportedChannelMask, GetNotifier().SignalIfFirst(OT_CHANGED_SUPPORTED_CHANNEL_MASK));
mSupportedChannelMask = newMask;
GetNotifier().Signal(OT_CHANGED_SUPPORTED_CHANNEL_MASK);
exit:
return;
}
otError Mac::SetNetworkName(const char *aNetworkName)
{
return SetNetworkName(aNetworkName, OT_NETWORK_NAME_MAX_SIZE + 1);
}
otError Mac::SetNetworkName(const char *aBuffer, uint8_t aLength)
{
otError error = OT_ERROR_NONE;
uint8_t newLen = static_cast<uint8_t>(strnlen(aBuffer, aLength));
VerifyOrExit(newLen <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(newLen != strlen(mNetworkName.m8) || memcmp(mNetworkName.m8, aBuffer, newLen) != 0,
GetNotifier().SignalIfFirst(OT_CHANGED_THREAD_NETWORK_NAME));
memcpy(mNetworkName.m8, aBuffer, newLen);
mNetworkName.m8[newLen] = 0;
GetNotifier().Signal(OT_CHANGED_THREAD_NETWORK_NAME);
exit:
return error;
}
void Mac::SetPanId(PanId aPanId)
{
VerifyOrExit(mPanId != aPanId, GetNotifier().SignalIfFirst(OT_CHANGED_THREAD_PANID));
mPanId = aPanId;
mSubMac.SetPanId(mPanId);
GetNotifier().Signal(OT_CHANGED_THREAD_PANID);
exit:
return;
}
void Mac::SetExtendedPanId(const otExtendedPanId &aExtendedPanId)
{
VerifyOrExit(memcmp(mExtendedPanId.m8, aExtendedPanId.m8, sizeof(mExtendedPanId)) != 0,
GetNotifier().SignalIfFirst(OT_CHANGED_THREAD_EXT_PANID));
mExtendedPanId = aExtendedPanId;
GetNotifier().Signal(OT_CHANGED_THREAD_EXT_PANID);
exit:
return;
}
otError Mac::SendFrameRequest(void)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(!mPendingTransmitData && (mOperation != kOperationTransmitData), error = OT_ERROR_ALREADY);
StartOperation(kOperationTransmitData);
exit:
return error;
}
otError Mac::SendOutOfBandFrameRequest(otRadioFrame *aOobFrame)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aOobFrame != NULL, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(!mPendingTransmitOobFrame && (mOperation != kOperationTransmitOutOfBandFrame),
error = OT_ERROR_ALREADY);
mOobFrame = static_cast<Frame *>(aOobFrame);
StartOperation(kOperationTransmitOutOfBandFrame);
exit:
return error;
}
void Mac::UpdateIdleMode(void)
{
bool shouldSleep = !mRxOnWhenIdle && !mPromiscuous;
VerifyOrExit(mOperation == kOperationIdle);
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (mShouldDelaySleep)
{
mTimer.Start(kSleepDelay);
mShouldDelaySleep = false;
mDelayingSleep = true;
otLogDebgMac("Idle mode: Sleep delayed");
}
if (mDelayingSleep)
{
shouldSleep = false;
}
#endif
if (shouldSleep)
{
mSubMac.Sleep();
otLogDebgMac("Idle mode: Radio sleeping");
}
else
{
mSubMac.Receive(mRadioChannel);
otLogDebgMac("Idle mode: Radio receiving on channel %d", mRadioChannel);
}
exit:
return;
}
void Mac::StartOperation(Operation aOperation)
{
if (aOperation != kOperationIdle)
{
otLogDebgMac("Request to start operation \"%s\"", OperationToString(aOperation));
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (mDelayingSleep)
{
otLogDebgMac("Canceling sleep delay");
mTimer.Stop();
mDelayingSleep = false;
mShouldDelaySleep = false;
}
#endif
}
switch (aOperation)
{
case kOperationIdle:
break;
case kOperationActiveScan:
mPendingActiveScan = true;
break;
case kOperationEnergyScan:
mPendingEnergyScan = true;
break;
case kOperationTransmitBeacon:
mPendingTransmitBeacon = true;
break;
case kOperationTransmitData:
mPendingTransmitData = true;
break;
case kOperationWaitingForData:
mPendingWaitingForData = true;
break;
case kOperationTransmitOutOfBandFrame:
mPendingTransmitOobFrame = true;
break;
}
if (mOperation == kOperationIdle)
{
mOperationTask.Post();
}
}
void Mac::HandleOperationTask(Tasklet &aTasklet)
{
aTasklet.GetOwner<Mac>().PerformNextOperation();
}
void Mac::PerformNextOperation(void)
{
VerifyOrExit(mOperation == kOperationIdle);
if (!mEnabled)
{
mPendingWaitingForData = false;
mPendingTransmitOobFrame = false;
mPendingActiveScan = false;
mPendingEnergyScan = false;
mPendingTransmitBeacon = false;
mPendingTransmitData = false;
mTimer.Stop();
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
mDelayingSleep = false;
mShouldDelaySleep = false;
#endif
ExitNow();
}
// `WaitingForData` should be checked before any other pending
// operations since radio should remain in receive mode after
// a data poll ack indicating a pending frame from parent.
if (mPendingWaitingForData)
{
mPendingWaitingForData = false;
mOperation = kOperationWaitingForData;
}
else if (mPendingTransmitOobFrame)
{
mPendingTransmitOobFrame = false;
mOperation = kOperationTransmitOutOfBandFrame;
}
else if (mPendingActiveScan)
{
mPendingActiveScan = false;
mOperation = kOperationActiveScan;
}
else if (mPendingEnergyScan)
{
mPendingEnergyScan = false;
mOperation = kOperationEnergyScan;
}
else if (mPendingTransmitBeacon)
{
mPendingTransmitBeacon = false;
mOperation = kOperationTransmitBeacon;
}
else if (mPendingTransmitData)
{
mPendingTransmitData = false;
mOperation = kOperationTransmitData;
}
if (mOperation != kOperationIdle)
{
otLogDebgMac("Starting operation \"%s\"", OperationToString(mOperation));
}
switch (mOperation)
{
case kOperationIdle:
UpdateIdleMode();
break;
case kOperationActiveScan:
PerformActiveScan();
break;
case kOperationEnergyScan:
PerformEnergyScan();
break;
case kOperationTransmitBeacon:
case kOperationTransmitData:
case kOperationTransmitOutOfBandFrame:
BeginTransmit();
break;
case kOperationWaitingForData:
mSubMac.Receive(mRadioChannel);
break;
}
exit:
return;
}
void Mac::FinishOperation(void)
{
otLogDebgMac("Finishing operation \"%s\"", OperationToString(mOperation));
mOperation = kOperationIdle;
}
void Mac::GenerateNonce(const ExtAddress &aAddress, uint32_t aFrameCounter, uint8_t aSecurityLevel, uint8_t *aNonce)
{
// source address
for (int i = 0; i < 8; i++)
{
aNonce[i] = aAddress.m8[i];
}
aNonce += 8;
// frame counter
aNonce[0] = (aFrameCounter >> 24) & 0xff;
aNonce[1] = (aFrameCounter >> 16) & 0xff;
aNonce[2] = (aFrameCounter >> 8) & 0xff;
aNonce[3] = (aFrameCounter >> 0) & 0xff;
aNonce += 4;
// security level
aNonce[0] = aSecurityLevel;
}
void Mac::SendBeaconRequest(Frame &aFrame)
{
// initialize MAC header
uint16_t fcf = Frame::kFcfFrameMacCmd | Frame::kFcfDstAddrShort | Frame::kFcfSrcAddrNone;
aFrame.InitMacHeader(fcf, Frame::kSecNone);
aFrame.SetDstPanId(kShortAddrBroadcast);
aFrame.SetDstAddr(kShortAddrBroadcast);
aFrame.SetCommandId(Frame::kMacCmdBeaconRequest);
otLogInfoMac("Sending Beacon Request");
}
void Mac::SendBeacon(Frame &aFrame)
{
uint8_t numUnsecurePorts;
uint8_t beaconLength;
uint16_t fcf;
Beacon * beacon = NULL;
BeaconPayload *beaconPayload = NULL;
// initialize MAC header
fcf = Frame::kFcfFrameBeacon | Frame::kFcfDstAddrNone | Frame::kFcfSrcAddrExt;
aFrame.InitMacHeader(fcf, Frame::kSecNone);
aFrame.SetSrcPanId(mPanId);
aFrame.SetSrcAddr(GetExtAddress());
// write payload
beacon = reinterpret_cast<Beacon *>(aFrame.GetPayload());
beacon->Init();
beaconLength = sizeof(*beacon);
beaconPayload = reinterpret_cast<BeaconPayload *>(beacon->GetPayload());
if (GetNetif().GetKeyManager().GetSecurityPolicyFlags() & OT_SECURITY_POLICY_BEACONS)
{
beaconPayload->Init();
// set the Joining Permitted flag
GetNetif().GetIp6Filter().GetUnsecurePorts(numUnsecurePorts);
if (numUnsecurePorts)
{
beaconPayload->SetJoiningPermitted();
}
else
{
beaconPayload->ClearJoiningPermitted();
}
beaconPayload->SetNetworkName(mNetworkName.m8);
beaconPayload->SetExtendedPanId(mExtendedPanId.m8);
beaconLength += sizeof(*beaconPayload);
}
aFrame.SetPayloadLength(beaconLength);
LogBeacon("Sending", *beaconPayload);
}
bool Mac::ShouldSendBeacon(void) const
{
bool shouldSend = false;
VerifyOrExit(mEnabled);
shouldSend = IsBeaconEnabled();
#if OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_WHEN_JOINABLE
if (!shouldSend)
{
// When `ENABLE_BEACON_RSP_WHEN_JOINABLE` feature is enabled,
// the device should transmit IEEE 802.15.4 Beacons in response
// to IEEE 802.15.4 Beacon Requests even while the device is not
// router capable and detached (i.e., `IsBeaconEnabled()` is
// false) but only if it is in joinable state (unsecure port
// list is not empty).
uint8_t numUnsecurePorts;
GetNetif().GetIp6Filter().GetUnsecurePorts(numUnsecurePorts);
shouldSend = (numUnsecurePorts != 0);
}
#endif
exit:
return shouldSend;
}
void Mac::ProcessTransmitAesCcm(Frame &aFrame, const ExtAddress *aExtAddress)
{
uint32_t frameCounter = 0;
uint8_t securityLevel;
uint8_t nonce[kNonceSize];
uint8_t tagLength;
Crypto::AesCcm aesCcm;
otError error;
aFrame.GetSecurityLevel(securityLevel);
aFrame.GetFrameCounter(frameCounter);
GenerateNonce(*aExtAddress, frameCounter, securityLevel, nonce);
aesCcm.SetKey(aFrame.GetAesKey(), 16);
tagLength = aFrame.GetFooterLength() - Frame::kFcsSize;
error = aesCcm.Init(aFrame.GetHeaderLength(), aFrame.GetPayloadLength(), tagLength, nonce, sizeof(nonce));
assert(error == OT_ERROR_NONE);
aesCcm.Header(aFrame.GetHeader(), aFrame.GetHeaderLength());
aesCcm.Payload(aFrame.GetPayload(), aFrame.GetPayload(), aFrame.GetPayloadLength(), true);
aesCcm.Finalize(aFrame.GetFooter(), &tagLength);
}
void Mac::ProcessTransmitSecurity(Frame &aFrame, bool aProcessAesCcm)
{
KeyManager & keyManager = GetNetif().GetKeyManager();
uint8_t keyIdMode;
const ExtAddress *extAddress = NULL;
if (aFrame.GetSecurityEnabled() == false)
{
ExitNow();
}
aFrame.GetKeyIdMode(keyIdMode);
switch (keyIdMode)
{
case Frame::kKeyIdMode0:
aFrame.SetAesKey(keyManager.GetKek());
extAddress = &GetExtAddress();
if (!aFrame.IsARetransmission())
{
aFrame.SetFrameCounter(keyManager.GetKekFrameCounter());
keyManager.IncrementKekFrameCounter();
}
break;
case Frame::kKeyIdMode1:
aFrame.SetAesKey(keyManager.GetCurrentMacKey());
extAddress = &GetExtAddress();
// If the frame is marked as a retransmission, `MeshForwarder` which
// prepared the frame should set the frame counter and key id to the
// same values used in the earlier transmit attempt. For a new frame (not
// a retransmission), we get a new frame counter and key id from the key
// manager.
if (!aFrame.IsARetransmission())
{
aFrame.SetFrameCounter(keyManager.GetMacFrameCounter());
keyManager.IncrementMacFrameCounter();
aFrame.SetKeyId((keyManager.GetCurrentKeySequence() & 0x7f) + 1);
}
break;
case Frame::kKeyIdMode2:
{
const uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff};
aFrame.SetAesKey(sMode2Key);
mKeyIdMode2FrameCounter++;
aFrame.SetFrameCounter(mKeyIdMode2FrameCounter);
aFrame.SetKeySource(keySource);
aFrame.SetKeyId(0xff);
extAddress = static_cast<const ExtAddress *>(&sMode2ExtAddress);
break;
}
default:
assert(false);
break;
}
if (aProcessAesCcm)
{
ProcessTransmitAesCcm(aFrame, extAddress);
}
exit:
return;
}
void Mac::BeginTransmit(void)
{
otError error = OT_ERROR_NONE;
bool applyTransmitSecurity = true;
bool processTransmitAesCcm = true;
Frame & sendFrame = mSubMac.GetTransmitFrame();
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
uint8_t timeIeOffset = 0;
#endif
VerifyOrExit(mEnabled, error = OT_ERROR_ABORT);
switch (mOperation)
{
case kOperationActiveScan:
mSubMac.SetPanId(kPanIdBroadcast);
sendFrame.SetChannel(mScanChannel);
SendBeaconRequest(sendFrame);
sendFrame.SetSequence(0);
sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
sendFrame.SetMaxFrameRetries(kMaxFrameRetriesDirect);
break;
case kOperationTransmitBeacon:
sendFrame.SetChannel(mRadioChannel);
SendBeacon(sendFrame);
sendFrame.SetSequence(mBeaconSequence++);
sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
sendFrame.SetMaxFrameRetries(kMaxFrameRetriesDirect);
break;
case kOperationTransmitData:
sendFrame.SetChannel(mRadioChannel);
SuccessOrExit(error = GetNetif().GetMeshForwarder().HandleFrameRequest(sendFrame));
// If the frame is marked as a retransmission, then data sequence number is already set.
if (!sendFrame.IsARetransmission())
{
sendFrame.SetSequence(mDataSequence);
}
break;
case kOperationTransmitOutOfBandFrame:
sendFrame.CopyFrom(*mOobFrame);
applyTransmitSecurity = false;
break;
default:
assert(false);
break;
}
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
timeIeOffset = GetTimeIeOffset(sendFrame);
sendFrame.SetTimeIeOffset(timeIeOffset);
if (timeIeOffset != 0)
{
// Transmit security will be processed after time IE content is updated.
processTransmitAesCcm = false;
sendFrame.SetTimeSyncSeq(GetNetif().GetTimeSync().GetTimeSyncSeq());
sendFrame.SetNetworkTimeOffset(GetNetif().GetTimeSync().GetNetworkTimeOffset());
}
#endif
if (applyTransmitSecurity)
{
// Security Processing
ProcessTransmitSecurity(sendFrame, processTransmitAesCcm);
}
mBroadcastTransmitCount = 0;
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (!mRxOnWhenIdle && !mPromiscuous)
{
mShouldDelaySleep = sendFrame.GetFramePending();
otLogDebgMac("Delay sleep for pending tx");
}
#endif
error = mSubMac.Send();
exit:
if (error != OT_ERROR_NONE)
{
HandleTransmitDone(sendFrame, NULL, OT_ERROR_ABORT);
}
}
void Mac::RecordCcaStatus(bool aCcaSuccess, uint8_t aChannel)
{
if (!aCcaSuccess)
{
mCounters.mTxErrCca++;
}
// Only track the CCA success rate for frame transmissions
// on the PAN channel.
if (aChannel == mPanChannel)
{
if (mCcaSampleCount < kMaxCcaSampleCount)
{
mCcaSampleCount++;
}
mCcaSuccessRateTracker.AddSample(aCcaSuccess, mCcaSampleCount);
}
}
void Mac::RecordFrameTransmitStatus(const Frame &aFrame,
const Frame *aAckFrame,
otError aError,
uint8_t aRetryCount,
bool aWillRetx)
{
bool ackRequested = aFrame.GetAckRequest();
Address dstAddr;
Neighbor *neighbor;
aFrame.GetDstAddr(dstAddr);
neighbor = GetNetif().GetMle().GetNeighbor(dstAddr);
#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
// Record frame transmission success/failure state (for a neighbor).
if ((neighbor != NULL) && ackRequested)
{
bool frameTxSuccess = true;
// CCA or abort errors are excluded from frame tx error
// rate tracking, since when they occur, the frame is
// not actually sent over the air.
switch (aError)
{
case OT_ERROR_NO_ACK:
frameTxSuccess = false;
// Fall through
case OT_ERROR_NONE:
neighbor->GetLinkInfo().AddFrameTxStatus(frameTxSuccess);
break;
default:
break;
}
}
#endif // OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
// Log frame transmission failure.
if (aError != OT_ERROR_NONE)
{
LogFrameTxFailure(aFrame, aError, aRetryCount);
otDumpDebgMac("TX ERR", aFrame.GetHeader(), 16);
if (aWillRetx)
{
mCounters.mTxRetry++;
// Since this failed transmission will be retried by `SubMac` layer
// there is no need to update any other MAC counter. MAC counters
// are updated on the final transmission attempt.
ExitNow();
}
}
// Update neighbor's RSSI link info from the received Ack.
if ((aError == OT_ERROR_NONE) && ackRequested && (aAckFrame != NULL) && (neighbor != NULL))
{
neighbor->GetLinkInfo().AddRss(GetNoiseFloor(), aAckFrame->GetRssi());
}
// Update MAC counters.
mCounters.mTxTotal++;
if (aError == OT_ERROR_ABORT)
{
mCounters.mTxErrAbort++;
}
if (aError == OT_ERROR_CHANNEL_ACCESS_FAILURE)
{
mCounters.mTxErrBusyChannel++;
}
if (ackRequested)
{
mCounters.mTxAckRequested++;
if (aError == OT_ERROR_NONE)
{
mCounters.mTxAcked++;
}
}
else
{
mCounters.mTxNoAckRequested++;
}
if (dstAddr.IsBroadcast())
{
mCounters.mTxBroadcast++;
}
else
{
mCounters.mTxUnicast++;
}
exit:
return;
}
void Mac::HandleTransmitDone(Frame &aFrame, Frame *aAckFrame, otError aError)
{
Address dstAddr;
// Determine whether to re-transmit a broadcast frame.
aFrame.GetDstAddr(dstAddr);
if (dstAddr.IsBroadcast())
{
mBroadcastTransmitCount++;
if (mBroadcastTransmitCount < kTxNumBcast)
{
mSubMac.Send();
ExitNow();
}
mBroadcastTransmitCount = 0;
}
// Determine next action based on current operation.
switch (mOperation)
{
case kOperationActiveScan:
mCounters.mTxBeaconRequest++;
mTimer.Start(mScanDuration);
break;
case kOperationTransmitBeacon:
mCounters.mTxBeacon++;
FinishOperation();
PerformNextOperation();
break;
case kOperationTransmitData:
if (aFrame.IsDataRequestCommand())
{
if (mEnabled && (aError == OT_ERROR_NONE) && aFrame.GetAckRequest() && (aAckFrame != NULL) &&
aAckFrame->GetFramePending())
{
mTimer.Start(kDataPollTimeout);
StartOperation(kOperationWaitingForData);
}
mCounters.mTxDataPoll++;
}
else
{
mCounters.mTxData++;
}
if (!aFrame.IsARetransmission())
{
mDataSequence++;
}
otDumpDebgMac("TX", aFrame.GetHeader(), aFrame.GetLength());
FinishOperation();
GetNetif().GetMeshForwarder().HandleSentFrame(aFrame, aError);
PerformNextOperation();
break;
case kOperationTransmitOutOfBandFrame:
FinishOperation();
PerformNextOperation();
break;
default:
assert(false);
break;
}
exit:
return;
}
void Mac::HandleTimer(Timer &aTimer)
{
aTimer.GetOwner<Mac>().HandleTimer();
}
void Mac::HandleTimer(void)
{
switch (mOperation)
{
case kOperationActiveScan:
PerformActiveScan();
break;
case kOperationWaitingForData:
otLogDebgMac("Data poll timeout");
FinishOperation();
GetNetif().GetMeshForwarder().GetDataPollManager().HandlePollTimeout();
PerformNextOperation();
break;
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
case kOperationIdle:
if (mDelayingSleep)
{
otLogDebgMac("Sleep delay timeout expired");
mDelayingSleep = false;
UpdateIdleMode();
}
break;
#endif
default:
assert(false);
break;
}
}
otError Mac::ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor)
{
KeyManager & keyManager = GetNetif().GetKeyManager();
otError error = OT_ERROR_NONE;
uint8_t securityLevel;
uint8_t keyIdMode;
uint32_t frameCounter;
uint8_t nonce[kNonceSize];
uint8_t tag[Frame::kMaxMicSize];
uint8_t tagLength;
uint8_t keyid;
uint32_t keySequence = 0;
const uint8_t * macKey;
const ExtAddress *extAddress;
Crypto::AesCcm aesCcm;
if (aFrame.GetSecurityEnabled() == false)
{
ExitNow();
}
aFrame.GetSecurityLevel(securityLevel);
aFrame.GetFrameCounter(frameCounter);
otLogDebgMac("Frame counter %u", frameCounter);
aFrame.GetKeyIdMode(keyIdMode);
switch (keyIdMode)
{
case Frame::kKeyIdMode0:
VerifyOrExit((macKey = keyManager.GetKek()) != NULL, error = OT_ERROR_SECURITY);
extAddress = &aSrcAddr.GetExtended();
break;
case Frame::kKeyIdMode1:
VerifyOrExit(aNeighbor != NULL, error = OT_ERROR_SECURITY);
aFrame.GetKeyId(keyid);
keyid--;
if (keyid == (keyManager.GetCurrentKeySequence() & 0x7f))
{
// same key index
keySequence = keyManager.GetCurrentKeySequence();
macKey = keyManager.GetCurrentMacKey();
}
else if (keyid == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f))
{
// previous key index
keySequence = keyManager.GetCurrentKeySequence() - 1;
macKey = keyManager.GetTemporaryMacKey(keySequence);
}
else if (keyid == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f))
{
// next key index
keySequence = keyManager.GetCurrentKeySequence() + 1;
macKey = keyManager.GetTemporaryMacKey(keySequence);
}
else
{
ExitNow(error = OT_ERROR_SECURITY);
}
// If the frame is from a neighbor not in valid state (e.g., it is from a child being
// restored), skip the key sequence and frame counter checks but continue to verify
// the tag/MIC. Such a frame is later filtered in `RxDoneTask` which only allows MAC
// Data Request frames from a child being restored.
if (aNeighbor->GetState() == Neighbor::kStateValid)
{
if (keySequence < aNeighbor->GetKeySequence())
{
ExitNow(error = OT_ERROR_SECURITY);
}
else if (keySequence == aNeighbor->GetKeySequence())
{
if ((frameCounter + 1) < aNeighbor->GetLinkFrameCounter())
{
ExitNow(error = OT_ERROR_SECURITY);
}
else if ((frameCounter + 1) == aNeighbor->GetLinkFrameCounter())
{
// drop duplicated frames
ExitNow(error = OT_ERROR_DUPLICATED);
}
}
}
extAddress = &aSrcAddr.GetExtended();
break;
case Frame::kKeyIdMode2:
macKey = sMode2Key;
extAddress = static_cast<const ExtAddress *>(&sMode2ExtAddress);
break;
default:
ExitNow(error = OT_ERROR_SECURITY);
break;
}
GenerateNonce(*extAddress, frameCounter, securityLevel, nonce);
tagLength = aFrame.GetFooterLength() - Frame::kFcsSize;
aesCcm.SetKey(macKey, 16);
error = aesCcm.Init(aFrame.GetHeaderLength(), aFrame.GetPayloadLength(), tagLength, nonce, sizeof(nonce));
VerifyOrExit(error == OT_ERROR_NONE, error = OT_ERROR_SECURITY);
aesCcm.Header(aFrame.GetHeader(), aFrame.GetHeaderLength());
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
aesCcm.Payload(aFrame.GetPayload(), aFrame.GetPayload(), aFrame.GetPayloadLength(), false);
#else
// for fuzz tests, execute AES but do not alter the payload
uint8_t fuzz[OT_RADIO_FRAME_MAX_SIZE];
aesCcm.Payload(fuzz, aFrame.GetPayload(), aFrame.GetPayloadLength(), false);
#endif
aesCcm.Finalize(tag, &tagLength);
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
VerifyOrExit(memcmp(tag, aFrame.GetFooter(), tagLength) == 0, error = OT_ERROR_SECURITY);
#endif
if ((keyIdMode == Frame::kKeyIdMode1) && (aNeighbor->GetState() == Neighbor::kStateValid))
{
if (aNeighbor->GetKeySequence() != keySequence)
{
aNeighbor->SetKeySequence(keySequence);
aNeighbor->SetMleFrameCounter(0);
}
aNeighbor->SetLinkFrameCounter(frameCounter + 1);
if (keySequence > keyManager.GetCurrentKeySequence())
{
keyManager.SetCurrentKeySequence(keySequence);
}
}
exit:
return error;
}
void Mac::HandleReceivedFrame(Frame *aFrame, otError aError)
{
ThreadNetif &netif = GetNetif();
Address srcaddr;
Address dstaddr;
PanId panid;
Neighbor * neighbor;
bool receive = false;
otError error = aError;
#if OPENTHREAD_ENABLE_MAC_FILTER
int8_t rssi = OT_MAC_FILTER_FIXED_RSS_DISABLED;
#endif // OPENTHREAD_ENABLE_MAC_FILTER
mCounters.mRxTotal++;
VerifyOrExit(error == OT_ERROR_NONE);
VerifyOrExit(aFrame != NULL, error = OT_ERROR_NO_FRAME_RECEIVED);
VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE);
// Ensure we have a valid frame before attempting to read any contents of
// the buffer received from the radio.
SuccessOrExit(error = aFrame->ValidatePsdu());
aFrame->GetSrcAddr(srcaddr);
aFrame->GetDstAddr(dstaddr);
neighbor = netif.GetMle().GetNeighbor(srcaddr);
// Destination Address Filtering
switch (dstaddr.GetType())
{
case Address::kTypeNone:
break;
case Address::kTypeShort:
aFrame->GetDstPanId(panid);
VerifyOrExit((panid == kShortAddrBroadcast || panid == mPanId) &&
((mRxOnWhenIdle && dstaddr.IsBroadcast()) || dstaddr.GetShort() == GetShortAddress()),
error = OT_ERROR_DESTINATION_ADDRESS_FILTERED);
// Allow multicasts from neighbor routers if FTD
if (neighbor == NULL && dstaddr.IsBroadcast() && netif.GetMle().IsFullThreadDevice())
{
neighbor = netif.GetMle().GetRxOnlyNeighborRouter(srcaddr);
}
break;
case Address::kTypeExtended:
aFrame->GetDstPanId(panid);
VerifyOrExit(panid == mPanId && dstaddr.GetExtended() == GetExtAddress(),
error = OT_ERROR_DESTINATION_ADDRESS_FILTERED);
break;
}
// Source Address Filtering
switch (srcaddr.GetType())
{
case Address::kTypeNone:
break;
case Address::kTypeShort:
otLogDebgMac("Received frame from short address 0x%04x", srcaddr.GetShort());
if (neighbor == NULL)
{
ExitNow(error = OT_ERROR_UNKNOWN_NEIGHBOR);
}
srcaddr.SetExtended(neighbor->GetExtAddress());
// fall through
case Address::kTypeExtended:
// Duplicate Address Protection
if (srcaddr.GetExtended() == GetExtAddress())
{
ExitNow(error = OT_ERROR_INVALID_SOURCE_ADDRESS);
}
#if OPENTHREAD_ENABLE_MAC_FILTER
// Source filter Processing. Check if filtered out by whitelist or blacklist.
SuccessOrExit(error = mFilter.Apply(srcaddr.GetExtended(), rssi));
// override with the rssi in setting
if (rssi != OT_MAC_FILTER_FIXED_RSS_DISABLED)
{
aFrame->SetRssi(rssi);
}
#endif // OPENTHREAD_ENABLE_MAC_FILTER
break;
}
// Increment counters
if (dstaddr.IsBroadcast())
{
// Broadcast frame
mCounters.mRxBroadcast++;
}
else
{
// Unicast frame
mCounters.mRxUnicast++;
}
// Security Processing
error = ProcessReceiveSecurity(*aFrame, srcaddr, neighbor);
switch (error)
{
case OT_ERROR_DUPLICATED:
// Allow a duplicate received frame pass, only if the
// current operation is `kOperationWaitingForData` (i.e.,
// the sleepy device is waiting to receive a frame after
// a data poll ack from parent indicating there is a
// pending frame for it). This ensures that the sleepy
// device goes to sleep faster and avoids a data poll
// timeout.
//
// Note that `error` is checked again later after the
// operation `kOperationWaitingForData` is processed
// so the duplicate frame will not be passed to next
// layer (`MeshForwarder`).
VerifyOrExit(mOperation == kOperationWaitingForData);
// Fall through
case OT_ERROR_NONE:
break;
default:
ExitNow();
}
netif.GetMeshForwarder().GetDataPollManager().CheckFramePending(*aFrame);
#if OPENTHREAD_CONFIG_HEADER_IE_SUPPORT
if (aFrame->GetVersion() == Frame::kFcfFrameVersion2015)
{
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
ProcessTimeIe(*aFrame);
#endif
}
#endif // OPENTHREAD_CONFIG_HEADER_IE_SUPPORT
if (neighbor != NULL)
{
#if OPENTHREAD_ENABLE_MAC_FILTER
// make assigned rssi to take effect quickly
if (rssi != OT_MAC_FILTER_FIXED_RSS_DISABLED)
{
neighbor->GetLinkInfo().Clear();
}
#endif // OPENTHREAD_ENABLE_MAC_FILTER
neighbor->GetLinkInfo().AddRss(GetNoiseFloor(), aFrame->GetRssi());
if (aFrame->GetSecurityEnabled() == true)
{
switch (neighbor->GetState())
{
case Neighbor::kStateValid:
break;
case Neighbor::kStateRestored:
case Neighbor::kStateChildUpdateRequest:
// Only accept a "MAC Data Request" frame from a child being restored.
VerifyOrExit(aFrame->IsDataRequestCommand(), error = OT_ERROR_DROP);
break;
default:
ExitNow(error = OT_ERROR_UNKNOWN_NEIGHBOR);
}
}
}
switch (mOperation)
{
case kOperationActiveScan:
if (aFrame->GetType() == Frame::kFcfFrameBeacon)
{
mCounters.mRxBeacon++;
mActiveScanHandler(GetInstance(), aFrame);
ExitNow();
}
// Fall-through
case kOperationEnergyScan:
// We can possibly receive a data frame while either active or
// energy scan is ongoing. We continue to process the frame only
// if the current scan channel matches `mPanChannel`.
VerifyOrExit(mScanChannel == mPanChannel, mCounters.mRxOther++);
break;
case kOperationWaitingForData:
if (!dstaddr.IsNone())
{
mTimer.Stop();
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (!mRxOnWhenIdle && !mPromiscuous && aFrame->GetFramePending())
{
mShouldDelaySleep = true;
otLogDebgMac("Delay sleep for pending rx");
}
#endif
FinishOperation();
PerformNextOperation();
}
SuccessOrExit(error);
break;
default:
break;
}
switch (aFrame->GetType())
{
case Frame::kFcfFrameMacCmd:
if (HandleMacCommand(*aFrame) == OT_ERROR_DROP)
{
ExitNow(error = OT_ERROR_NONE);
}
receive = true;
break;
case Frame::kFcfFrameBeacon:
mCounters.mRxBeacon++;
receive = true;
break;
case Frame::kFcfFrameData:
mCounters.mRxData++;
receive = true;
break;
default:
mCounters.mRxOther++;
break;
}
if (receive)
{
otDumpDebgMac("RX", aFrame->GetHeader(), aFrame->GetLength());
netif.GetMeshForwarder().HandleReceivedFrame(*aFrame);
}
exit:
if (error != OT_ERROR_NONE)
{
LogFrameRxFailure(aFrame, error);
switch (error)
{
case OT_ERROR_SECURITY:
mCounters.mRxErrSec++;
break;
case OT_ERROR_FCS:
mCounters.mRxErrFcs++;
break;
case OT_ERROR_NO_FRAME_RECEIVED:
mCounters.mRxErrNoFrame++;
break;
case OT_ERROR_UNKNOWN_NEIGHBOR:
mCounters.mRxErrUnknownNeighbor++;
break;
case OT_ERROR_INVALID_SOURCE_ADDRESS:
mCounters.mRxErrInvalidSrcAddr++;
break;
case OT_ERROR_ADDRESS_FILTERED:
mCounters.mRxAddressFiltered++;
break;
case OT_ERROR_DESTINATION_ADDRESS_FILTERED:
mCounters.mRxDestAddrFiltered++;
break;
case OT_ERROR_DUPLICATED:
mCounters.mRxDuplicated++;
break;
default:
mCounters.mRxErrOther++;
break;
}
}
}
otError Mac::HandleMacCommand(Frame &aFrame)
{
otError error = OT_ERROR_NONE;
uint8_t commandId;
aFrame.GetCommandId(commandId);
switch (commandId)
{
case Frame::kMacCmdBeaconRequest:
mCounters.mRxBeaconRequest++;
otLogInfoMac("Received Beacon Request");
if (ShouldSendBeacon())
{
StartOperation(kOperationTransmitBeacon);
}
ExitNow(error = OT_ERROR_DROP);
case Frame::kMacCmdDataRequest:
mCounters.mRxDataPoll++;
break;
default:
mCounters.mRxOther++;
break;
}
exit:
return error;
}
void Mac::SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext)
{
mSubMac.SetPcapCallback(aPcapCallback, aCallbackContext);
}
void Mac::SetPromiscuous(bool aPromiscuous)
{
mPromiscuous = aPromiscuous;
otPlatRadioSetPromiscuous(&GetInstance(), aPromiscuous);
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
mDelayingSleep = false;
mShouldDelaySleep = false;
#endif
mSubMac.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous);
UpdateIdleMode();
}
void Mac::SetEnabled(bool aEnable)
{
mEnabled = aEnable;
}
void Mac::ResetCounters(void)
{
memset(&mCounters, 0, sizeof(mCounters));
}
int8_t Mac::GetNoiseFloor(void)
{
return otPlatRadioGetReceiveSensitivity(&GetInstance());
}
const char *Mac::OperationToString(Operation aOperation)
{
const char *retval = "";
switch (aOperation)
{
case kOperationIdle:
retval = "Idle";
break;
case kOperationActiveScan:
retval = "ActiveScan";
break;
case kOperationEnergyScan:
retval = "EnergyScan";
break;
case kOperationTransmitBeacon:
retval = "TransmitBeacon";
break;
case kOperationTransmitData:
retval = "TransmitData";
break;
case kOperationWaitingForData:
retval = "WaitingForData";
break;
case kOperationTransmitOutOfBandFrame:
retval = "TransmitOobFrame";
break;
}
return retval;
}
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
void Mac::LogFrameRxFailure(const Frame *aFrame, otError aError) const
{
otLogLevel logLevel;
switch (aError)
{
case OT_ERROR_ABORT:
case OT_ERROR_NO_FRAME_RECEIVED:
case OT_ERROR_DESTINATION_ADDRESS_FILTERED:
logLevel = OT_LOG_LEVEL_DEBG;
break;
default:
logLevel = OT_LOG_LEVEL_INFO;
break;
}
if (aFrame == NULL)
{
otLogMac(logLevel, "Frame rx failed, error:%s", otThreadErrorToString(aError));
}
else
{
otLogMac(logLevel, "Frame rx failed, error:%s, %s", otThreadErrorToString(aError),
aFrame->ToInfoString().AsCString());
}
}
void Mac::LogFrameTxFailure(const Frame &aFrame, otError aError, uint8_t aRetryCount) const
{
otLogInfoMac("Frame tx failed, error:%s, retries:%d/%d, %s", otThreadErrorToString(aError), aRetryCount,
aFrame.GetMaxFrameRetries(), aFrame.ToInfoString().AsCString());
}
void Mac::LogBeacon(const char *aActionText, const BeaconPayload &aBeaconPayload) const
{
otLogInfoMac("%s Beacon, %s", aActionText, aBeaconPayload.ToInfoString().AsCString());
}
#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
void Mac::LogFrameRxFailure(const Frame *, otError) const
{
}
void Mac::LogBeacon(const char *, const BeaconPayload &) const
{
}
void Mac::LogFrameTxFailure(const Frame &, otError, uint8_t) const
{
}
#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
void Mac::ProcessTimeIe(Frame &aFrame)
{
TimeIe *timeIe = reinterpret_cast<TimeIe *>(aFrame.GetTimeIe());
VerifyOrExit(timeIe != NULL);
aFrame.SetNetworkTimeOffset(static_cast<int64_t>(timeIe->GetTime()) - static_cast<int64_t>(aFrame.GetTimestamp()));
aFrame.SetTimeSyncSeq(timeIe->GetSequence());
exit:
return;
}
uint8_t Mac::GetTimeIeOffset(Frame &aFrame)
{
uint8_t offset = 0;
uint8_t *base = aFrame.GetPsdu();
uint8_t *cur = NULL;
cur = aFrame.GetTimeIe();
VerifyOrExit(cur != NULL);
cur += sizeof(VendorIeHeader);
offset = static_cast<uint8_t>(cur - base);
exit:
return offset;
}
#endif // OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
} // namespace Mac
} // namespace ot