blob: 1f0005b8783e1269beef96183feffebf2369805f [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 the subset of IEEE 802.15.4 primitives required for Thread.
*/
#define WPP_NAME "mac.tmh"
#include <openthread/config.h>
#include "mac.hpp"
#include "utils/wrap_string.h"
#include <openthread/platform/random.h>
#include <openthread/platform/usec-alarm.h>
#include "openthread-instance.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/encoding.hpp"
#include "common/logging.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 uint8_t sExtendedPanidInit[] = {0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe};
static const char sNetworkNameInit[] = "OpenThread";
#ifdef _WIN32
const uint32_t kMinBackoffSum = kMinBackoff + (kUnitBackoffPeriod *OT_RADIO_SYMBOL_TIME * (1 << kMinBE)) / 1000;
const uint32_t kMaxBackoffSum = kMinBackoff + (kUnitBackoffPeriod *OT_RADIO_SYMBOL_TIME * (1 << kMaxBE)) / 1000;
static_assert(kMinBackoffSum > 0, "The min backoff value should be greater than zero!");
#endif
void Mac::StartCsmaBackoff(void)
{
if (RadioSupportsCsmaBackoff())
{
// If the radio supports CSMA back off logic, immediately schedule the send.
HandleBeginTransmit();
}
else
{
uint32_t backoffExponent = kMinBE + mTransmitAttempts + mCsmaAttempts;
uint32_t backoff;
if (backoffExponent > kMaxBE)
{
backoffExponent = kMaxBE;
}
backoff = (otPlatRandomGet() % (1UL << backoffExponent));
backoff *= (static_cast<uint32_t>(kUnitBackoffPeriod) * OT_RADIO_SYMBOL_TIME);
#if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_BACKOFF_TIMER
otPlatUsecAlarmStartAt(GetInstance(), otPlatUsecAlarmGetNow(), backoff, &Mac::HandleBeginTransmit, this);
#else // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_BACKOFF_TIMER
mBackoffTimer.Start(backoff / 1000UL);
#endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_BACKOFF_TIMER
}
}
Mac::Mac(ThreadNetif &aThreadNetif):
ThreadNetifLocator(aThreadNetif),
mMacTimer(aThreadNetif.GetIp6().mTimerScheduler, &Mac::HandleMacTimer, this),
#if !OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_BACKOFF_TIMER
mBackoffTimer(aThreadNetif.GetIp6().mTimerScheduler, &Mac::HandleBeginTransmit, this),
#endif
mReceiveTimer(aThreadNetif.GetIp6().mTimerScheduler, &Mac::HandleReceiveTimer, this),
mShortAddress(kShortAddrInvalid),
mPanId(kPanIdBroadcast),
mChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL),
mMaxTransmitPower(OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER),
mSendHead(NULL),
mSendTail(NULL),
mReceiveHead(NULL),
mReceiveTail(NULL),
mOperation(kOperationNone),
mBeaconSequence(static_cast<uint8_t>(otPlatRandomGet())),
mDataSequence(static_cast<uint8_t>(otPlatRandomGet())),
mRxOnWhenIdle(false),
mCsmaAttempts(0),
mTransmitAttempts(0),
mTransmitBeacon(false),
mBeaconsEnabled(false),
mPendingScanRequest(kScanTypeNone),
mScanChannel(OT_RADIO_CHANNEL_MIN),
mScanChannels(0xff),
mScanDuration(0),
mScanContext(NULL),
mActiveScanHandler(NULL), // initialize mActiveScanHandler and mEnergyScanHandler union
mEnergyScanCurrentMaxRssi(kInvalidRssiValue),
mEnergyScanSampleRssiTask(aThreadNetif.GetIp6().mTaskletScheduler, &Mac::HandleEnergyScanSampleRssi, this),
mPcapCallback(NULL),
mPcapCallbackContext(NULL),
mWhitelist(),
mBlacklist(),
mTxFrame(static_cast<Frame *>(otPlatRadioGetTransmitBuffer(aThreadNetif.GetInstance()))),
mKeyIdMode2FrameCounter(0),
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
mDelaySleep(false),
#endif
mWaitingForData(false)
{
GenerateExtAddress(&mExtAddress);
memset(&mCounters, 0, sizeof(otMacCounters));
SetExtendedPanId(sExtendedPanidInit);
SetNetworkName(sNetworkNameInit);
SetPanId(mPanId);
SetExtAddress(mExtAddress);
SetShortAddress(mShortAddress);
otPlatRadioEnable(GetInstance());
}
otError Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveScanHandler aHandler, void *aContext)
{
otError error;
SuccessOrExit(error = Scan(kScanTypeActive, aScanChannels, aScanDuration, aContext));
mActiveScanHandler = aHandler;
exit:
return error;
}
otError Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergyScanHandler aHandler, void *aContext)
{
otError error;
SuccessOrExit(error = Scan(kScanTypeEnergy, aScanChannels, aScanDuration, aContext));
mEnergyScanHandler = aHandler;
exit:
return error;
}
otError Mac::Scan(ScanType aScanType, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext)
{
otError error = OT_ERROR_NONE;
VerifyOrExit((mOperation != kOperationActiveScan) && (mOperation != kOperationEnergyScan) &&
(mPendingScanRequest == kScanTypeNone), error = OT_ERROR_BUSY);
mScanContext = aContext;
mScanChannels = (aScanChannels == 0) ? static_cast<uint32_t>(kScanChannelsAll) : aScanChannels;
mScanDuration = (aScanDuration == 0) ? static_cast<uint16_t>(kScanDurationDefault) : aScanDuration;
mScanChannel = OT_RADIO_CHANNEL_MIN;
mScanChannels >>= OT_RADIO_CHANNEL_MIN;
while ((mScanChannels & 1) == 0)
{
mScanChannels >>= 1;
mScanChannel++;
}
mPendingScanRequest = aScanType;
StartOperation();
exit:
return error;
}
bool Mac::IsActiveScanInProgress(void)
{
return (mOperation == kOperationActiveScan) || (mPendingScanRequest == kScanTypeActive);
}
bool Mac::IsEnergyScanInProgress(void)
{
return (mOperation == kOperationEnergyScan) || (mPendingScanRequest == kScanTypeEnergy);
}
bool Mac::IsInTransmitState(void)
{
return (mOperation == kOperationTransmitData) || (mOperation == kOperationTransmitBeacon);
}
otError Mac::ConvertBeaconToActiveScanResult(Frame *aBeaconFrame, otActiveScanResult &aResult)
{
otError error = OT_ERROR_NONE;
Address address;
Beacon *beacon = NULL;
BeaconPayload *beaconPayload = NULL;
uint8_t payloadLength;
char stringBuffer[BeaconPayload::kInfoStringSize];
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.mLength == sizeof(address.mExtAddress), error = OT_ERROR_PARSE);
memcpy(&aResult.mExtAddress, &address.mExtAddress, sizeof(aResult.mExtAddress));
aBeaconFrame->GetSrcPanId(aResult.mPanId);
aResult.mChannel = aBeaconFrame->GetChannel();
aResult.mRssi = aBeaconFrame->GetPower();
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(), sizeof(aResult.mNetworkName));
memcpy(&aResult.mExtendedPanId, beaconPayload->GetExtendedPanId(), sizeof(aResult.mExtendedPanId));
}
otLogInfoMac(GetInstance(), "Received Beacon, %s", beaconPayload->ToInfoString(stringBuffer, sizeof(stringBuffer)));
OT_UNUSED_VARIABLE(stringBuffer);
exit:
return error;
}
void Mac::StartEnergyScan(void)
{
if (!(otPlatRadioGetCaps(GetInstance()) & OT_RADIO_CAPS_ENERGY_SCAN))
{
mEnergyScanCurrentMaxRssi = kInvalidRssiValue;
mMacTimer.Start(mScanDuration);
mEnergyScanSampleRssiTask.Post();
RadioReceive(mScanChannel);
}
else
{
otError error = otPlatRadioEnergyScan(GetInstance(), mScanChannel, mScanDuration);
if (error != OT_ERROR_NONE)
{
// Cancel scan
mEnergyScanHandler(mScanContext, NULL);
FinishOperation();
}
}
}
extern "C" void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi)
{
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (aInstance->mLinkRaw.IsEnabled())
{
aInstance->mLinkRaw.InvokeEnergyScanDone(aEnergyScanMaxRssi);
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
aInstance->mThreadNetif.GetMac().EnergyScanDone(aEnergyScanMaxRssi);
}
}
void Mac::EnergyScanDone(int8_t aEnergyScanMaxRssi)
{
// Trigger a energy scan handler callback if necessary
if (aEnergyScanMaxRssi != kInvalidRssiValue)
{
otEnergyScanResult result;
result.mChannel = mScanChannel;
result.mMaxRssi = aEnergyScanMaxRssi;
mEnergyScanHandler(mScanContext, &result);
}
// Update to the next scan channel
do
{
mScanChannels >>= 1;
mScanChannel++;
// If we have scanned all the channels, then fire the final callback
// and start the next transmission task
if (mScanChannels == 0 || mScanChannel > OT_RADIO_CHANNEL_MAX)
{
RadioReceive(mChannel);
mEnergyScanHandler(mScanContext, NULL);
FinishOperation();
ExitNow();
}
}
while ((mScanChannels & 1) == 0);
// Start scanning the next channel
StartEnergyScan();
exit:
return;
}
void Mac::HandleEnergyScanSampleRssi(Tasklet &aTasklet)
{
GetOwner(aTasklet).HandleEnergyScanSampleRssi();
}
void Mac::HandleEnergyScanSampleRssi(void)
{
int8_t rssi;
VerifyOrExit(mOperation == kOperationEnergyScan);
rssi = otPlatRadioGetRssi(GetInstance());
if (rssi != kInvalidRssiValue)
{
if ((mEnergyScanCurrentMaxRssi == kInvalidRssiValue) || (rssi > mEnergyScanCurrentMaxRssi))
{
mEnergyScanCurrentMaxRssi = rssi;
}
}
mEnergyScanSampleRssiTask.Post();
exit:
return;
}
otError Mac::RegisterReceiver(Receiver &aReceiver)
{
assert(mReceiveTail != &aReceiver && aReceiver.mNext == NULL);
if (mReceiveTail == NULL)
{
mReceiveHead = &aReceiver;
mReceiveTail = &aReceiver;
}
else
{
mReceiveTail->mNext = &aReceiver;
mReceiveTail = &aReceiver;
}
return OT_ERROR_NONE;
}
void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
{
mRxOnWhenIdle = aRxOnWhenIdle;
// Update idle state if no ongoing operation.
StartOperation();
}
void Mac::GenerateExtAddress(ExtAddress *aExtAddress)
{
for (size_t i = 0; i < sizeof(ExtAddress); i++)
{
aExtAddress->m8[i] = static_cast<uint8_t>(otPlatRandomGet());
}
aExtAddress->SetGroup(false);
aExtAddress->SetLocal(true);
}
void Mac::SetExtAddress(const ExtAddress &aExtAddress)
{
uint8_t buf[sizeof(aExtAddress)];
otLogFuncEntry();
for (size_t i = 0; i < sizeof(buf); i++)
{
buf[i] = aExtAddress.m8[7 - i];
}
otPlatRadioSetExtendedAddress(GetInstance(), buf);
mExtAddress = aExtAddress;
otLogFuncExit();
}
void Mac::GetHashMacAddress(ExtAddress *aHashMacAddress)
{
Crypto::Sha256 sha256;
uint8_t buf[Crypto::Sha256::kHashSize];
otLogFuncEntry();
otPlatRadioGetIeeeEui64(GetInstance(), buf);
sha256.Start();
sha256.Update(buf, OT_EXT_ADDRESS_SIZE);
sha256.Finish(buf);
memcpy(aHashMacAddress->m8, buf, OT_EXT_ADDRESS_SIZE);
aHashMacAddress->SetLocal(true);
otLogFuncExitMsg("%llX", HostSwap64(*reinterpret_cast<uint64_t *>(aHashMacAddress)));
}
otError Mac::SetShortAddress(ShortAddress aShortAddress)
{
otLogFuncEntryMsg("%d", aShortAddress);
mShortAddress = aShortAddress;
otPlatRadioSetShortAddress(GetInstance(), aShortAddress);
otLogFuncExit();
return OT_ERROR_NONE;
}
otError Mac::SetChannel(uint8_t aChannel)
{
otLogFuncEntryMsg("%d", aChannel);
mChannel = aChannel;
// Update channel if no ongoing operation.
StartOperation();
otLogFuncExit();
return OT_ERROR_NONE;
}
otError Mac::SetNetworkName(const char *aNetworkName)
{
otError error = OT_ERROR_NONE;
otLogFuncEntryMsg("%s", aNetworkName);
VerifyOrExit(strlen(aNetworkName) <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
(void)strlcpy(mNetworkName.m8, aNetworkName, sizeof(mNetworkName));
exit:
otLogFuncExitErr(error);
return error;
}
otError Mac::SetPanId(PanId aPanId)
{
otLogFuncEntryMsg("%d", aPanId);
mPanId = aPanId;
otPlatRadioSetPanId(GetInstance(), mPanId);
otLogFuncExit();
return OT_ERROR_NONE;
}
otError Mac::SetExtendedPanId(const uint8_t *aExtPanId)
{
memcpy(mExtendedPanId.m8, aExtPanId, sizeof(mExtendedPanId));
return OT_ERROR_NONE;
}
otError Mac::SendFrameRequest(Sender &aSender)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mSendTail != &aSender && aSender.mNext == NULL, error = OT_ERROR_ALREADY);
if (mSendHead == NULL)
{
mSendHead = &aSender;
mSendTail = &aSender;
}
else
{
mSendTail->mNext = &aSender;
mSendTail = &aSender;
}
StartOperation();
exit:
return error;
}
void Mac::PutRadioInIdleMode(void)
{
if (mRxOnWhenIdle || mReceiveTimer.IsRunning() || otPlatRadioGetPromiscuous(GetInstance()))
{
RadioReceive(mChannel);
}
else
{
RadioSleep();
}
}
void Mac::StartOperation(void)
{
VerifyOrExit(mOperation == kOperationNone);
VerifyOrExit(!mWaitingForData);
if (mPendingScanRequest == kScanTypeActive)
{
mPendingScanRequest = kScanTypeNone;
mOperation = kOperationActiveScan;
StartCsmaBackoff();
}
else if (mPendingScanRequest == kScanTypeEnergy)
{
mPendingScanRequest = kScanTypeNone;
mOperation = kOperationEnergyScan;
StartEnergyScan();
}
else if (mTransmitBeacon)
{
mTransmitBeacon = false;
mOperation = kOperationTransmitBeacon;
StartCsmaBackoff();
}
else if (mSendHead != NULL)
{
mOperation = kOperationTransmitData;
StartCsmaBackoff();
}
else
{
PutRadioInIdleMode();
}
exit:
return;
}
void Mac::FinishOperation(void)
{
mOperation = kOperationNone;
PutRadioInIdleMode();
StartOperation();
}
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(GetInstance(), "Sending Beacon Request");
}
void Mac::SendBeacon(Frame &aFrame)
{
uint8_t numUnsecurePorts;
uint8_t beaconLength;
uint16_t fcf;
Beacon *beacon = NULL;
BeaconPayload *beaconPayload = NULL;
char stringBuffer[BeaconPayload::kInfoStringSize];
// initialize MAC header
fcf = Frame::kFcfFrameBeacon | Frame::kFcfDstAddrNone | Frame::kFcfSrcAddrExt;
aFrame.InitMacHeader(fcf, Frame::kSecNone);
aFrame.SetSrcPanId(mPanId);
aFrame.SetSrcAddr(mExtAddress);
// 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);
otLogInfoMac(GetInstance(), "Sending Beacon, %s", beaconPayload->ToInfoString(stringBuffer, sizeof(stringBuffer)));
OT_UNUSED_VARIABLE(stringBuffer);
}
void Mac::ProcessTransmitSecurity(Frame &aFrame)
{
KeyManager &keyManager = GetNetif().GetKeyManager();
uint32_t frameCounter = 0;
uint8_t securityLevel;
uint8_t keyIdMode;
uint8_t nonce[kNonceSize];
uint8_t tagLength;
Crypto::AesCcm aesCcm;
const uint8_t *key = NULL;
const ExtAddress *extAddress = NULL;
if (aFrame.GetSecurityEnabled() == false)
{
ExitNow();
}
aFrame.GetKeyIdMode(keyIdMode);
switch (keyIdMode)
{
case Frame::kKeyIdMode0:
key = keyManager.GetKek();
extAddress = &mExtAddress;
if (!aFrame.IsARetransmission())
{
aFrame.SetFrameCounter(keyManager.GetKekFrameCounter());
keyManager.IncrementKekFrameCounter();
}
break;
case Frame::kKeyIdMode1:
key = keyManager.GetCurrentMacKey();
extAddress = &mExtAddress;
// If the frame is marked as a retransmission, the `Mac::Sender` 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};
key = sMode2Key;
mKeyIdMode2FrameCounter++;
aFrame.SetFrameCounter(mKeyIdMode2FrameCounter);
aFrame.SetKeySource(keySource);
aFrame.SetKeyId(0xff);
extAddress = static_cast<const ExtAddress *>(&sMode2ExtAddress);
break;
}
default:
assert(false);
break;
}
aFrame.GetSecurityLevel(securityLevel);
aFrame.GetFrameCounter(frameCounter);
GenerateNonce(*extAddress, frameCounter, securityLevel, nonce);
aesCcm.SetKey(key, 16);
tagLength = aFrame.GetFooterLength() - Frame::kFcsSize;
aesCcm.Init(aFrame.GetHeaderLength(), aFrame.GetPayloadLength(), tagLength, nonce, sizeof(nonce));
aesCcm.Header(aFrame.GetHeader(), aFrame.GetHeaderLength());
aesCcm.Payload(aFrame.GetPayload(), aFrame.GetPayload(), aFrame.GetPayloadLength(), true);
aesCcm.Finalize(aFrame.GetFooter(), &tagLength);
exit:
return;
}
void Mac::HandleBeginTransmit(void *aContext)
{
static_cast<Mac *>(aContext)->HandleBeginTransmit();
}
void Mac::HandleBeginTransmit(Timer &aTimer)
{
GetOwner(aTimer).HandleBeginTransmit();
}
void Mac::HandleBeginTransmit(void)
{
Frame &sendFrame(*mTxFrame);
otError error = OT_ERROR_NONE;
if (mCsmaAttempts == 0 && mTransmitAttempts == 0)
{
sendFrame.SetPower(mMaxTransmitPower);
switch (mOperation)
{
case kOperationActiveScan:
otPlatRadioSetPanId(GetInstance(), kPanIdBroadcast);
sendFrame.SetChannel(mScanChannel);
SendBeaconRequest(sendFrame);
sendFrame.SetSequence(0);
sendFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts);
break;
case kOperationTransmitBeacon:
sendFrame.SetChannel(mChannel);
SendBeacon(sendFrame);
sendFrame.SetSequence(mBeaconSequence++);
sendFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts);
break;
case kOperationTransmitData:
sendFrame.SetChannel(mChannel);
SuccessOrExit(error = mSendHead->HandleFrameRequest(sendFrame));
// If the frame is marked as a retransmission, then data sequence number is already set by the `Sender`.
if (!sendFrame.IsARetransmission())
{
sendFrame.SetSequence(mDataSequence);
}
break;
default:
assert(false);
break;
}
// Security Processing
ProcessTransmitSecurity(sendFrame);
if (sendFrame.GetPower() > mMaxTransmitPower)
{
sendFrame.SetPower(mMaxTransmitPower);
}
}
error = RadioReceive(sendFrame.GetChannel());
assert(error == OT_ERROR_NONE);
error = RadioTransmit(&sendFrame);
assert(error == OT_ERROR_NONE);
if (sendFrame.GetAckRequest() && !(otPlatRadioGetCaps(GetInstance()) & OT_RADIO_CAPS_ACK_TIMEOUT))
{
mMacTimer.Start(kAckTimeout);
otLogDebgMac(GetInstance(), "Ack timer start");
}
if (mPcapCallback)
{
sendFrame.mDidTX = true;
mPcapCallback(&sendFrame, mPcapCallbackContext);
}
exit:
if (error != OT_ERROR_NONE)
{
#if OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE
TransmitDoneTask(mTxFrame, false, OT_ERROR_ABORT);
#else
TransmitDoneTask(mTxFrame, NULL, OT_ERROR_ABORT);
#endif
}
}
#if OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE
extern "C" void otPlatRadioTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, bool aRxPending,
otError aError)
{
otLogFuncEntryMsg("%!otError!, aRxPending=%u", aError, aRxPending ? 1 : 0);
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (aInstance->mLinkRaw.IsEnabled())
{
aInstance->mLinkRaw.InvokeTransmitDone(aFrame, aRxPending, aError);
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
aInstance->mThreadNetif.GetMac().TransmitDoneTask(aFrame, aRxPending, aError);
}
otLogFuncExit();
}
void Mac::TransmitDoneTask(otRadioFrame *aFrame, bool aRxPending, otError aError)
{
mMacTimer.Stop();
mCounters.mTxTotal++;
Frame *frame = static_cast<Frame *>(aFrame);
Address addr;
frame->GetDstAddr(addr);
if (addr.mShortAddress == kShortAddrBroadcast)
{
// Broadcast frame
mCounters.mTxBroadcast++;
}
else
{
// Unicast frame
mCounters.mTxUnicast++;
}
if (aError == OT_ERROR_ABORT)
{
mCounters.mTxErrAbort++;
}
if (aError == OT_ERROR_CHANNEL_ACCESS_FAILURE)
{
mCounters.mTxErrCca++;
}
if (!RadioSupportsCsmaBackoff() &&
aError == OT_ERROR_CHANNEL_ACCESS_FAILURE &&
mCsmaAttempts < kMaxCSMABackoffs)
{
mCsmaAttempts++;
StartCsmaBackoff();
ExitNow();
}
mCsmaAttempts = 0;
switch (mOperation)
{
case kOperationTransmitData:
{
uint8_t commandId;
if ((frame->GetType() == Frame::kFcfFrameMacCmd) && (frame->GetCommandId(commandId) == OT_ERROR_NONE) &&
(commandId == Frame::kMacCmdDataRequest) && (aRxPending))
{
mWaitingForData = true;
mReceiveTimer.Start(kDataPollTimeout);
}
}
// fall through
case kOperationActiveScan:
case kOperationTransmitBeacon:
SentFrame(aError);
break;
default:
assert(false);
break;
}
exit:
return;
}
#else // #if OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE
extern "C" void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame,
otError aError)
{
otLogFuncEntryMsg("%!otError!", aError);
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (aInstance->mLinkRaw.IsEnabled())
{
aInstance->mLinkRaw.InvokeTransmitDone(aFrame, (static_cast<Frame *>(aAckFrame))->GetFramePending(), aError);
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
aInstance->mThreadNetif.GetMac().TransmitDoneTask(aFrame, aAckFrame, aError);
}
otLogFuncExit();
}
void Mac::TransmitDoneTask(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError)
{
Frame *txFrame = static_cast<Frame *>(aFrame);
Address addr;
bool framePending = false;
mMacTimer.Stop();
mCounters.mTxTotal++;
txFrame->GetDstAddr(addr);
if (aError == OT_ERROR_NONE && txFrame->GetAckRequest() && aAckFrame != NULL)
{
Frame *ackFrame = static_cast<Frame *>(aAckFrame);
Neighbor *neighbor;
framePending = ackFrame->GetFramePending();
neighbor = GetNetif().GetMle().GetNeighbor(addr);
if (neighbor != NULL)
{
neighbor->GetLinkInfo().AddRss(GetNoiseFloor(), ackFrame->GetPower());
}
}
if (addr.mShortAddress == kShortAddrBroadcast)
{
// Broadcast frame
mCounters.mTxBroadcast++;
}
else
{
// Unicast frame
mCounters.mTxUnicast++;
}
if (aError == OT_ERROR_ABORT)
{
mCounters.mTxErrAbort++;
}
if (aError == OT_ERROR_CHANNEL_ACCESS_FAILURE)
{
mCounters.mTxErrCca++;
}
if (!RadioSupportsCsmaBackoff() &&
aError == OT_ERROR_CHANNEL_ACCESS_FAILURE &&
mCsmaAttempts < kMaxCSMABackoffs)
{
mCsmaAttempts++;
StartCsmaBackoff();
ExitNow();
}
mCsmaAttempts = 0;
switch (mOperation)
{
case kOperationTransmitData:
{
uint8_t commandId;
if ((txFrame->GetType() == Frame::kFcfFrameMacCmd) && (txFrame->GetCommandId(commandId) == OT_ERROR_NONE) &&
(commandId == Frame::kMacCmdDataRequest) && (framePending))
{
mWaitingForData = true;
mReceiveTimer.Start(kDataPollTimeout);
}
}
// fall through
case kOperationActiveScan:
case kOperationTransmitBeacon:
SentFrame(aError);
break;
default:
assert(false);
break;
}
exit:
return;
}
#endif // OPENTHREAD_CONFIG_LEGACY_TRANSMIT_DONE
otError Mac::RadioTransmit(Frame *aSendFrame)
{
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (!mRxOnWhenIdle)
{
// Cancel delay sleep timer
mReceiveTimer.Stop();
// Delay sleep if we have another frame pending to transmit
mDelaySleep = aSendFrame->GetFramePending();
}
#endif
// Transmit packet
return otPlatRadioTransmit(GetInstance(), static_cast<otRadioFrame *>(aSendFrame));
}
otError Mac::RadioReceive(uint8_t aChannel)
{
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (!mRxOnWhenIdle)
{
// Cancel delay sleep timer
mReceiveTimer.Stop();
}
#endif
// Receive
return otPlatRadioReceive(GetInstance(), aChannel);
}
void Mac::RadioSleep(void)
{
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
if (mDelaySleep)
{
// Restart delay sleep timer
mReceiveTimer.Start(kSleepDelay);
}
else
#endif
{
otPlatRadioSleep(GetInstance());
}
}
void Mac::HandleMacTimer(Timer &aTimer)
{
GetOwner(aTimer).HandleMacTimer();
}
void Mac::HandleMacTimer(void)
{
Address addr;
switch (mOperation)
{
case kOperationActiveScan:
do
{
mScanChannels >>= 1;
mScanChannel++;
if (mScanChannels == 0 || mScanChannel > OT_RADIO_CHANNEL_MAX)
{
RadioReceive(mChannel);
otPlatRadioSetPanId(GetInstance(), mPanId);
mActiveScanHandler(mScanContext, NULL);
FinishOperation();
ExitNow();
}
}
while ((mScanChannels & 1) == 0);
RadioReceive(mScanChannel);
StartCsmaBackoff();
break;
case kOperationEnergyScan:
EnergyScanDone(mEnergyScanCurrentMaxRssi);
break;
case kOperationTransmitData:
otLogDebgMac(GetInstance(), "Ack timer fired");
RadioReceive(mChannel);
mCounters.mTxTotal++;
mTxFrame->GetDstAddr(addr);
if (addr.mShortAddress == kShortAddrBroadcast)
{
// Broadcast frame
mCounters.mTxBroadcast++;
}
else
{
// Unicast Frame
mCounters.mTxUnicast++;
}
SentFrame(OT_ERROR_NO_ACK);
break;
default:
assert(false);
break;
}
exit:
return;
}
void Mac::HandleReceiveTimer(Timer &aTimer)
{
GetOwner(aTimer).HandleReceiveTimer();
}
void Mac::HandleReceiveTimer(void)
{
if (mWaitingForData)
{
otLogDebgMac(GetInstance(), "Data poll timeout");
mWaitingForData = false;
for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext)
{
receiver->HandleDataPollTimeout();
}
}
StartOperation();
}
void Mac::SentFrame(otError aError)
{
Frame &sendFrame(*mTxFrame);
Sender *sender;
mTransmitAttempts++;
switch (aError)
{
case OT_ERROR_NONE:
break;
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
case OT_ERROR_ABORT:
case OT_ERROR_NO_ACK:
{
char stringBuffer[Frame::kInfoStringSize];
otLogInfoMac(GetInstance(), "Frame tx failed, error:%s, attempt:%d/%d, %s", otThreadErrorToString(aError),
mTransmitAttempts, sendFrame.GetMaxTxAttempts(),
sendFrame.ToInfoString(stringBuffer, sizeof(stringBuffer)));
otDumpDebgMac(GetInstance(), "TX ERR", sendFrame.GetHeader(), 16);
if (!RadioSupportsRetries() &&
mTransmitAttempts < sendFrame.GetMaxTxAttempts())
{
StartCsmaBackoff();
mCounters.mTxRetry++;
ExitNow();
}
OT_UNUSED_VARIABLE(stringBuffer);
break;
}
default:
assert(false);
break;
}
mTransmitAttempts = 0;
mCsmaAttempts = 0;
if (sendFrame.GetAckRequest())
{
mCounters.mTxAckRequested++;
if (aError == OT_ERROR_NONE)
{
mCounters.mTxAcked++;
}
}
else
{
mCounters.mTxNoAckRequested++;
}
switch (mOperation)
{
case kOperationActiveScan:
mCounters.mTxBeaconRequest++;
mMacTimer.Start(mScanDuration);
break;
case kOperationTransmitBeacon:
mCounters.mTxBeacon++;
FinishOperation();
break;
case kOperationTransmitData:
if (mReceiveTimer.IsRunning())
{
mCounters.mTxDataPoll++;
}
else
{
mCounters.mTxData++;
}
sender = mSendHead;
mSendHead = mSendHead->mNext;
if (mSendHead == NULL)
{
mSendTail = NULL;
}
sender->mNext = NULL;
if (!sendFrame.IsARetransmission())
{
mDataSequence++;
}
otDumpDebgMac(GetInstance(), "TX", sendFrame.GetHeader(), sendFrame.GetLength());
sender->HandleSentFrame(sendFrame, aError);
FinishOperation();
break;
default:
assert(false);
break;
}
exit:
return;
}
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;
aFrame.SetSecurityValid(false);
if (aFrame.GetSecurityEnabled() == false)
{
ExitNow();
}
aFrame.GetSecurityLevel(securityLevel);
aFrame.GetFrameCounter(frameCounter);
otLogDebgMac(GetInstance(), "Frame counter %u", frameCounter);
aFrame.GetKeyIdMode(keyIdMode);
switch (keyIdMode)
{
case Frame::kKeyIdMode0:
VerifyOrExit((macKey = keyManager.GetKek()) != NULL, error = OT_ERROR_SECURITY);
extAddress = &aSrcAddr.mExtAddress;
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.mExtAddress;
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);
aesCcm.Init(aFrame.GetHeaderLength(), aFrame.GetPayloadLength(), tagLength, nonce, sizeof(nonce));
aesCcm.Header(aFrame.GetHeader(), aFrame.GetHeaderLength());
aesCcm.Payload(aFrame.GetPayload(), aFrame.GetPayload(), aFrame.GetPayloadLength(), false);
aesCcm.Finalize(tag, &tagLength);
VerifyOrExit(memcmp(tag, aFrame.GetFooter(), tagLength) == 0, error = OT_ERROR_SECURITY);
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);
}
}
aFrame.SetSecurityValid(true);
exit:
return error;
}
extern "C" void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError)
{
otLogFuncEntryMsg("%!otError!", aError);
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (aInstance->mLinkRaw.IsEnabled())
{
aInstance->mLinkRaw.InvokeReceiveDone(aFrame, aError);
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
aInstance->mThreadNetif.GetMac().ReceiveDoneTask(static_cast<Frame *>(aFrame), aError);
}
otLogFuncExit();
}
void Mac::ReceiveDoneTask(Frame *aFrame, otError aError)
{
Address srcaddr;
Address dstaddr;
PanId panid;
Neighbor *neighbor;
otMacWhitelistEntry *whitelistEntry;
int8_t rssi;
bool receive = false;
uint8_t commandId;
bool scheduleNextTrasmission = false;
otError error = aError;
mCounters.mRxTotal++;
VerifyOrExit(error == OT_ERROR_NONE);
VerifyOrExit(aFrame != NULL, error = OT_ERROR_NO_FRAME_RECEIVED);
aFrame->SetSecurityValid(false);
if (mPcapCallback)
{
aFrame->mDidTX = false;
mPcapCallback(aFrame, mPcapCallbackContext);
}
// 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);
neighbor = GetNetif().GetMle().GetNeighbor(srcaddr);
switch (srcaddr.mLength)
{
case 0:
break;
case sizeof(ShortAddress):
otLogDebgMac(GetInstance(), "Received frame from short address 0x%04x", srcaddr.mShortAddress);
if (neighbor == NULL)
{
ExitNow(error = OT_ERROR_UNKNOWN_NEIGHBOR);
}
srcaddr.mLength = sizeof(srcaddr.mExtAddress);
srcaddr.mExtAddress = neighbor->GetExtAddress();
break;
case sizeof(ExtAddress):
break;
default:
ExitNow(error = OT_ERROR_INVALID_SOURCE_ADDRESS);
}
// Duplicate Address Protection
if (memcmp(&srcaddr.mExtAddress, &mExtAddress, sizeof(srcaddr.mExtAddress)) == 0)
{
ExitNow(error = OT_ERROR_INVALID_SOURCE_ADDRESS);
}
// Source Whitelist Processing
if (srcaddr.mLength != 0 && mWhitelist.IsEnabled())
{
VerifyOrExit((whitelistEntry = mWhitelist.Find(srcaddr.mExtAddress)) != NULL, error = OT_ERROR_WHITELIST_FILTERED);
if (mWhitelist.GetFixedRssi(*whitelistEntry, rssi) == OT_ERROR_NONE)
{
aFrame->mPower = rssi;
}
}
// Source Blacklist Processing
if (srcaddr.mLength != 0 && mBlacklist.IsEnabled())
{
VerifyOrExit((mBlacklist.Find(srcaddr.mExtAddress)) == NULL, error = OT_ERROR_BLACKLIST_FILTERED);
}
// Destination Address Filtering
aFrame->GetDstAddr(dstaddr);
switch (dstaddr.mLength)
{
case 0:
break;
case sizeof(ShortAddress):
aFrame->GetDstPanId(panid);
VerifyOrExit((panid == kShortAddrBroadcast || panid == mPanId) &&
((mRxOnWhenIdle && dstaddr.mShortAddress == kShortAddrBroadcast) ||
dstaddr.mShortAddress == mShortAddress), error = OT_ERROR_DESTINATION_ADDRESS_FILTERED);
break;
case sizeof(ExtAddress):
aFrame->GetDstPanId(panid);
VerifyOrExit(panid == mPanId &&
memcmp(&dstaddr.mExtAddress, &mExtAddress, sizeof(dstaddr.mExtAddress)) == 0,
error = OT_ERROR_DESTINATION_ADDRESS_FILTERED);
break;
}
// Increment counters
if (dstaddr.mShortAddress == kShortAddrBroadcast)
{
// Broadcast frame
mCounters.mRxBroadcast++;
}
else
{
// Unicast frame
mCounters.mRxUnicast++;
}
// Security Processing
SuccessOrExit(error = ProcessReceiveSecurity(*aFrame, srcaddr, neighbor));
if (neighbor != NULL)
{
neighbor->GetLinkInfo().AddRss(GetNoiseFloor(), aFrame->mPower);
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->GetType() == Frame::kFcfFrameMacCmd, error = OT_ERROR_DROP);
VerifyOrExit(aFrame->GetCommandId(commandId) == OT_ERROR_NONE, error = OT_ERROR_DROP);
VerifyOrExit(commandId == Frame::kMacCmdDataRequest, error = OT_ERROR_DROP);
break;
default:
ExitNow(error = OT_ERROR_UNKNOWN_NEIGHBOR);
}
}
}
switch (mOperation)
{
case kOperationActiveScan:
if (aFrame->GetType() == Frame::kFcfFrameBeacon)
{
mCounters.mRxBeacon++;
mActiveScanHandler(mScanContext, aFrame);
}
else
{
mCounters.mRxOther++;
}
break;
default:
if (dstaddr.mLength != 0)
{
mWaitingForData = false;
if (!mRxOnWhenIdle)
{
mReceiveTimer.Stop();
scheduleNextTrasmission = true;
#if OPENTHREAD_CONFIG_STAY_AWAKE_BETWEEN_FRAGMENTS
mDelaySleep = aFrame->GetFramePending();
#endif
}
}
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(GetInstance(), "RX", aFrame->GetHeader(), aFrame->GetLength());
for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext)
{
receiver->HandleReceivedFrame(*aFrame);
}
}
break;
}
exit:
if (scheduleNextTrasmission)
{
StartOperation();
}
if (error != OT_ERROR_NONE)
{
if (aFrame == NULL)
{
otLogInfoMac(GetInstance(), "Frame rx failed, error:%s", otThreadErrorToString(error));
}
else
{
char stringBuffer[Frame::kInfoStringSize];
otLogInfoMac(GetInstance(), "Frame rx failed, error:%s, %s", otThreadErrorToString(error),
aFrame->ToInfoString(stringBuffer, sizeof(stringBuffer)));
OT_UNUSED_VARIABLE(stringBuffer);
}
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_WHITELIST_FILTERED:
mCounters.mRxWhitelistFiltered++;
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(GetInstance(), "Received Beacon Request");
if ((mBeaconsEnabled)
#if OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_IF_JOINABLE
&& (IsBeaconJoinable())
#endif // OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_IF_JOINABLE
)
{
mTransmitBeacon = true;
StartOperation();
}
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)
{
mPcapCallback = aPcapCallback;
mPcapCallbackContext = aCallbackContext;
}
bool Mac::IsPromiscuous(void)
{
return otPlatRadioGetPromiscuous(GetInstance());
}
void Mac::SetPromiscuous(bool aPromiscuous)
{
otPlatRadioSetPromiscuous(GetInstance(), aPromiscuous);
StartOperation();
}
bool Mac::RadioSupportsCsmaBackoff(void)
{
/* Check either of the following conditions:
* 1) Radio provides the CSMA backoff capability (i.e., `OT_RADIO_CAPS_CSMA_BACKOFF` bit is set) or;
* 2) It provides `OT_RADIO_CAPS_TRANSMIT_RETRIES` which indicates support for MAC retries along with CSMA backoff.
*/
return (otPlatRadioGetCaps(GetInstance()) & (OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF)) != 0;
}
bool Mac::RadioSupportsRetries(void)
{
return (otPlatRadioGetCaps(GetInstance()) & OT_RADIO_CAPS_TRANSMIT_RETRIES) != 0;
}
void Mac::FillMacCountersTlv(NetworkDiagnostic::MacCountersTlv &aMacCounters) const
{
aMacCounters.SetIfInUnknownProtos(mCounters.mRxOther);
aMacCounters.SetIfInErrors(mCounters.mRxErrNoFrame + mCounters.mRxErrUnknownNeighbor + mCounters.mRxErrInvalidSrcAddr +
mCounters.mRxErrSec + mCounters.mRxErrFcs + mCounters.mRxErrOther);
aMacCounters.SetIfOutErrors(mCounters.mTxErrCca);
aMacCounters.SetIfInUcastPkts(mCounters.mRxUnicast);
aMacCounters.SetIfInBroadcastPkts(mCounters.mRxBroadcast);
aMacCounters.SetIfInDiscards(mCounters.mRxWhitelistFiltered + mCounters.mRxDestAddrFiltered + mCounters.mRxDuplicated);
aMacCounters.SetIfOutUcastPkts(mCounters.mTxUnicast);
aMacCounters.SetIfOutBroadcastPkts(mCounters.mTxBroadcast);
aMacCounters.SetIfOutDiscards(0);
}
void Mac::ResetCounters(void)
{
memset(&mCounters, 0, sizeof(mCounters));
}
#if OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_IF_JOINABLE
bool Mac::IsBeaconJoinable(void)
{
uint8_t numUnsecurePorts;
bool joinable = false;
GetNetif().GetIp6Filter().GetUnsecurePorts(numUnsecurePorts);
if (numUnsecurePorts)
{
joinable = true;
}
else
{
joinable = false;
}
return joinable;
}
#endif // OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_IF_JOINABLE
Mac &Mac::GetOwner(const Context &aContext)
{
#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
Mac &mac = *static_cast<Mac *>(aContext.GetContext());
#else
Mac &mac = otGetInstance()->mThreadNetif.GetMac();
OT_UNUSED_VARIABLE(aContext);
#endif
return mac;
}
} // namespace Mac
} // namespace ot