| /* | |
| * 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" | |
| #ifdef OPENTHREAD_CONFIG_FILE | |
| #include OPENTHREAD_CONFIG_FILE | |
| #else | |
| #include <openthread-config.h> | |
| #endif | |
| #include <string.h> | |
| #include "openthread/platform/random.h" | |
| #include "openthread/platform/usec-alarm.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.hpp> | |
| #include <mac/mac_frame.hpp> | |
| #include <thread/mle_router.hpp> | |
| #include <thread/thread_netif.hpp> | |
| #include <openthread-instance.h> | |
| using Thread::Encoding::BigEndian::HostSwap64; | |
| namespace Thread { | |
| 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 *kPhyUsPerSymbol * (1 << kMinBE)) / 1000; | |
| const uint32_t kMaxBackoffSum = kMinBackoff + (kUnitBackoffPeriod *kPhyUsPerSymbol * (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 *= (kUnitBackoffPeriod * kPhyUsPerSymbol); | |
| #if OPENTHREAD_CONFIG_ENABLE_PLATFORM_USEC_BACKOFF_TIMER | |
| otPlatUsecAlarmTime now; | |
| otPlatUsecAlarmTime delay; | |
| otPlatUsecAlarmGetNow(&now); | |
| delay.mMs = backoff / 1000UL; | |
| delay.mUs = backoff - (delay.mMs * 1000UL); | |
| otPlatUsecAlarmStartAt(GetInstance(), &now, &delay, &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): | |
| 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), | |
| mNetif(aThreadNetif), | |
| mEnergyScanSampleRssiTask(aThreadNetif.GetIp6().mTaskletScheduler, &Mac::HandleEnergyScanSampleRssi, this), | |
| mWhitelist(), | |
| mBlacklist() | |
| { | |
| mState = kStateIdle; | |
| mRxOnWhenIdle = false; | |
| mCsmaAttempts = 0; | |
| mTransmitAttempts = 0; | |
| mTransmitBeacon = false; | |
| mBeaconsEnabled = false; | |
| mPendingScanRequest = kScanTypeNone; | |
| mScanChannel = kPhyMinChannel; | |
| mScanChannels = 0xff; | |
| mScanDuration = 0; | |
| mScanContext = NULL; | |
| mActiveScanHandler = NULL; | |
| mEnergyScanHandler = NULL; | |
| mEnergyScanCurrentMaxRssi = kInvalidRssiValue; | |
| mSendHead = NULL; | |
| mSendTail = NULL; | |
| mReceiveHead = NULL; | |
| mReceiveTail = NULL; | |
| mChannel = OPENTHREAD_CONFIG_DEFAULT_CHANNEL; | |
| mMaxTransmitPower = OPENTHREAD_CONFIG_DEFAULT_MAX_TRANSMIT_POWER; | |
| mPanId = kPanIdBroadcast; | |
| mShortAddress = kShortAddrInvalid; | |
| for (size_t i = 0; i < sizeof(mExtAddress); i++) | |
| { | |
| mExtAddress.m8[i] = static_cast<uint8_t>(otPlatRandomGet()); | |
| } | |
| mExtAddress.SetGroup(false); | |
| mExtAddress.SetLocal(true); | |
| memset(&mCounters, 0, sizeof(otMacCounters)); | |
| SetExtendedPanId(sExtendedPanidInit); | |
| SetNetworkName(sNetworkNameInit); | |
| SetPanId(mPanId); | |
| SetExtAddress(mExtAddress); | |
| SetShortAddress(kShortAddrInvalid); | |
| mBeaconSequence = static_cast<uint8_t>(otPlatRandomGet()); | |
| mDataSequence = static_cast<uint8_t>(otPlatRandomGet()); | |
| mPcapCallback = NULL; | |
| mPcapCallbackContext = NULL; | |
| otPlatRadioEnable(GetInstance()); | |
| mTxFrame = static_cast<Frame *>(otPlatRadioGetTransmitBuffer(GetInstance())); | |
| mKeyIdMode2FrameCounter = 0; | |
| } | |
| otInstance *Mac::GetInstance(void) | |
| { | |
| return mNetif.GetInstance(); | |
| } | |
| ThreadError Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveScanHandler aHandler, void *aContext) | |
| { | |
| ThreadError error; | |
| SuccessOrExit(error = Scan(kScanTypeActive, aScanChannels, aScanDuration, aContext)); | |
| mActiveScanHandler = aHandler; | |
| exit: | |
| return error; | |
| } | |
| ThreadError Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergyScanHandler aHandler, void *aContext) | |
| { | |
| ThreadError error; | |
| SuccessOrExit(error = Scan(kScanTypeEnergy, aScanChannels, aScanDuration, aContext)); | |
| mEnergyScanHandler = aHandler; | |
| exit: | |
| return error; | |
| } | |
| ThreadError Mac::Scan(ScanType aScanType, uint32_t aScanChannels, uint16_t aScanDuration, void *aContext) | |
| { | |
| ThreadError error = kThreadError_None; | |
| VerifyOrExit((mState != kStateActiveScan) && (mState != kStateEnergyScan) && (mPendingScanRequest == kScanTypeNone), | |
| error = kThreadError_Busy); | |
| mScanContext = aContext; | |
| mScanChannels = (aScanChannels == 0) ? static_cast<uint32_t>(kScanChannelsAll) : aScanChannels; | |
| mScanDuration = (aScanDuration == 0) ? static_cast<uint16_t>(kScanDurationDefault) : aScanDuration; | |
| mScanChannel = kPhyMinChannel; | |
| mScanChannels >>= kPhyMinChannel; | |
| while ((mScanChannels & 1) == 0) | |
| { | |
| mScanChannels >>= 1; | |
| mScanChannel++; | |
| } | |
| if (mState == kStateIdle) | |
| { | |
| if (aScanType == kScanTypeActive) | |
| { | |
| mState = kStateActiveScan; | |
| StartCsmaBackoff(); | |
| } | |
| else if (aScanType == kScanTypeEnergy) | |
| { | |
| StartEnergyScan(); | |
| } | |
| } | |
| else | |
| { | |
| mPendingScanRequest = aScanType; | |
| } | |
| exit: | |
| return error; | |
| } | |
| bool Mac::IsActiveScanInProgress(void) | |
| { | |
| return (mState == kStateActiveScan) || (mPendingScanRequest == kScanTypeActive); | |
| } | |
| bool Mac::IsEnergyScanInProgress(void) | |
| { | |
| return (mState == kStateEnergyScan) || (mPendingScanRequest == kScanTypeEnergy); | |
| } | |
| bool Mac::IsInTransmitState(void) | |
| { | |
| return (mState == kStateTransmitData) || (mState == kStateTransmitBeacon); | |
| } | |
| void Mac::StartEnergyScan(void) | |
| { | |
| mState = kStateEnergyScan; | |
| if (!(otPlatRadioGetCaps(GetInstance()) & kRadioCapsEnergyScan)) | |
| { | |
| mEnergyScanCurrentMaxRssi = kInvalidRssiValue; | |
| mMacTimer.Start(mScanDuration); | |
| mEnergyScanSampleRssiTask.Post(); | |
| NextOperation(); | |
| } | |
| else | |
| { | |
| ThreadError error = otPlatRadioEnergyScan(GetInstance(), mScanChannel, mScanDuration); | |
| if (error != kThreadError_None) | |
| { | |
| // Cancel scan | |
| mEnergyScanHandler(mScanContext, NULL); | |
| ScheduleNextTransmission(); | |
| } | |
| } | |
| } | |
| 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 > kPhyMaxChannel) | |
| { | |
| otPlatRadioReceive(GetInstance(), mChannel); | |
| mEnergyScanHandler(mScanContext, NULL); | |
| ScheduleNextTransmission(); | |
| ExitNow(); | |
| } | |
| } | |
| while ((mScanChannels & 1) == 0); | |
| // Start scanning the next channel | |
| StartEnergyScan(); | |
| exit: | |
| return; | |
| } | |
| void Mac::HandleEnergyScanSampleRssi(void *aContext) | |
| { | |
| static_cast<Mac *>(aContext)->HandleEnergyScanSampleRssi(); | |
| } | |
| void Mac::HandleEnergyScanSampleRssi(void) | |
| { | |
| int8_t rssi; | |
| VerifyOrExit(mState == kStateEnergyScan); | |
| rssi = otPlatRadioGetRssi(GetInstance()); | |
| if (rssi != kInvalidRssiValue) | |
| { | |
| if ((mEnergyScanCurrentMaxRssi == kInvalidRssiValue) || (rssi > mEnergyScanCurrentMaxRssi)) | |
| { | |
| mEnergyScanCurrentMaxRssi = rssi; | |
| } | |
| } | |
| mEnergyScanSampleRssiTask.Post(); | |
| exit: | |
| return; | |
| } | |
| ThreadError Mac::RegisterReceiver(Receiver &aReceiver) | |
| { | |
| assert(mReceiveTail != &aReceiver && aReceiver.mNext == NULL); | |
| if (mReceiveTail == NULL) | |
| { | |
| mReceiveHead = &aReceiver; | |
| mReceiveTail = &aReceiver; | |
| } | |
| else | |
| { | |
| mReceiveTail->mNext = &aReceiver; | |
| mReceiveTail = &aReceiver; | |
| } | |
| return kThreadError_None; | |
| } | |
| void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle) | |
| { | |
| mRxOnWhenIdle = aRxOnWhenIdle; | |
| if (mState == kStateIdle) | |
| { | |
| NextOperation(); | |
| } | |
| } | |
| 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))); | |
| } | |
| ThreadError Mac::SetShortAddress(ShortAddress aShortAddress) | |
| { | |
| otLogFuncEntryMsg("%d", aShortAddress); | |
| mShortAddress = aShortAddress; | |
| otPlatRadioSetShortAddress(GetInstance(), aShortAddress); | |
| otLogFuncExit(); | |
| return kThreadError_None; | |
| } | |
| ThreadError Mac::SetChannel(uint8_t aChannel) | |
| { | |
| otLogFuncEntryMsg("%d", aChannel); | |
| mChannel = aChannel; | |
| if (mState == kStateIdle) | |
| { | |
| NextOperation(); | |
| } | |
| otLogFuncExit(); | |
| return kThreadError_None; | |
| } | |
| ThreadError Mac::SetNetworkName(const char *aNetworkName) | |
| { | |
| ThreadError error = kThreadError_None; | |
| otLogFuncEntryMsg("%s", aNetworkName); | |
| VerifyOrExit(strlen(aNetworkName) <= OT_NETWORK_NAME_MAX_SIZE, error = kThreadError_InvalidArgs); | |
| (void)strlcpy(mNetworkName.m8, aNetworkName, sizeof(mNetworkName)); | |
| exit: | |
| otLogFuncExitErr(error); | |
| return error; | |
| } | |
| ThreadError Mac::SetPanId(PanId aPanId) | |
| { | |
| otLogFuncEntryMsg("%d", aPanId); | |
| mPanId = aPanId; | |
| otPlatRadioSetPanId(GetInstance(), mPanId); | |
| otLogFuncExit(); | |
| return kThreadError_None; | |
| } | |
| ThreadError Mac::SetExtendedPanId(const uint8_t *aExtPanId) | |
| { | |
| memcpy(mExtendedPanId.m8, aExtPanId, sizeof(mExtendedPanId)); | |
| return kThreadError_None; | |
| } | |
| ThreadError Mac::SendFrameRequest(Sender &aSender) | |
| { | |
| ThreadError error = kThreadError_None; | |
| VerifyOrExit(mSendTail != &aSender && aSender.mNext == NULL, error = kThreadError_Already); | |
| if (mSendHead == NULL) | |
| { | |
| mSendHead = &aSender; | |
| mSendTail = &aSender; | |
| } | |
| else | |
| { | |
| mSendTail->mNext = &aSender; | |
| mSendTail = &aSender; | |
| } | |
| if (mState == kStateIdle) | |
| { | |
| mState = kStateTransmitData; | |
| StartCsmaBackoff(); | |
| } | |
| exit: | |
| return error; | |
| } | |
| void Mac::NextOperation(void) | |
| { | |
| switch (mState) | |
| { | |
| case kStateActiveScan: | |
| case kStateEnergyScan: | |
| otPlatRadioReceive(GetInstance(), mScanChannel); | |
| break; | |
| default: | |
| if (mRxOnWhenIdle || mReceiveTimer.IsRunning() || otPlatRadioGetPromiscuous(GetInstance())) | |
| { | |
| otPlatRadioReceive(GetInstance(), mChannel); | |
| } | |
| else | |
| { | |
| otPlatRadioSleep(GetInstance()); | |
| } | |
| break; | |
| } | |
| } | |
| void Mac::ScheduleNextTransmission(void) | |
| { | |
| if (mPendingScanRequest == kScanTypeActive) | |
| { | |
| mPendingScanRequest = kScanTypeNone; | |
| mState = kStateActiveScan; | |
| StartCsmaBackoff(); | |
| } | |
| else if (mPendingScanRequest == kScanTypeEnergy) | |
| { | |
| mPendingScanRequest = kScanTypeNone; | |
| StartEnergyScan(); | |
| } | |
| else if (mTransmitBeacon) | |
| { | |
| mTransmitBeacon = false; | |
| mState = kStateTransmitBeacon; | |
| StartCsmaBackoff(); | |
| } | |
| else if (mSendHead != NULL) | |
| { | |
| mState = kStateTransmitData; | |
| StartCsmaBackoff(); | |
| } | |
| else | |
| { | |
| mState = kStateIdle; | |
| } | |
| NextOperation(); | |
| } | |
| 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); | |
| otLogDebgMac(GetInstance(), "Sent 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(mExtAddress); | |
| // write payload | |
| beacon = reinterpret_cast<Beacon *>(aFrame.GetPayload()); | |
| beacon->Init(); | |
| beaconLength = sizeof(*beacon); | |
| beaconPayload = reinterpret_cast<BeaconPayload *>(beacon->GetPayload()); | |
| if (mNetif.GetKeyManager().GetSecurityPolicyFlags() & OT_SECURITY_POLICY_BEACONS) | |
| { | |
| beaconPayload->Init(); | |
| // set the Joining Permitted flag | |
| mNetif.GetIp6Filter().GetUnsecurePorts(numUnsecurePorts); | |
| if (numUnsecurePorts) | |
| { | |
| beaconPayload->SetJoiningPermitted(); | |
| } | |
| else | |
| { | |
| beaconPayload->ClearJoiningPermitted(); | |
| } | |
| beaconPayload->SetNetworkName(mNetworkName.m8); | |
| beaconPayload->SetExtendedPanId(mExtendedPanId.m8); | |
| beaconLength += sizeof(*beaconPayload); | |
| } | |
| aFrame.SetPayloadLength(beaconLength); | |
| otLogDebgMac(GetInstance(), "Sent Beacon"); | |
| } | |
| void Mac::ProcessTransmitSecurity(Frame &aFrame) | |
| { | |
| 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 = mNetif.GetKeyManager().GetKek(); | |
| extAddress = &mExtAddress; | |
| if (!aFrame.IsARetransmission()) | |
| { | |
| aFrame.SetFrameCounter(mNetif.GetKeyManager().GetKekFrameCounter()); | |
| mNetif.GetKeyManager().IncrementKekFrameCounter(); | |
| } | |
| break; | |
| case Frame::kKeyIdMode1: | |
| key = mNetif.GetKeyManager().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(mNetif.GetKeyManager().GetMacFrameCounter()); | |
| mNetif.GetKeyManager().IncrementMacFrameCounter(); | |
| aFrame.SetKeyId((mNetif.GetKeyManager().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(void) | |
| { | |
| Frame &sendFrame(*mTxFrame); | |
| ThreadError error = kThreadError_None; | |
| if (mCsmaAttempts == 0 && mTransmitAttempts == 0) | |
| { | |
| sendFrame.SetPower(mMaxTransmitPower); | |
| switch (mState) | |
| { | |
| case kStateActiveScan: | |
| otPlatRadioSetPanId(GetInstance(), kPanIdBroadcast); | |
| sendFrame.SetChannel(mScanChannel); | |
| SendBeaconRequest(sendFrame); | |
| sendFrame.SetSequence(0); | |
| sendFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts); | |
| break; | |
| case kStateTransmitBeacon: | |
| sendFrame.SetChannel(mChannel); | |
| SendBeacon(sendFrame); | |
| sendFrame.SetSequence(mBeaconSequence++); | |
| sendFrame.SetMaxTxAttempts(kDirectFrameMacTxAttempts); | |
| break; | |
| case kStateTransmitData: | |
| 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 = otPlatRadioReceive(GetInstance(), sendFrame.GetChannel()); | |
| assert(error == kThreadError_None); | |
| error = otPlatRadioTransmit(GetInstance(), static_cast<RadioPacket *>(&sendFrame)); | |
| assert(error == kThreadError_None); | |
| if (sendFrame.GetAckRequest() && !(otPlatRadioGetCaps(GetInstance()) & kRadioCapsAckTimeout)) | |
| { | |
| mMacTimer.Start(kAckTimeout); | |
| otLogDebgMac(GetInstance(), "ack timer start"); | |
| } | |
| if (mPcapCallback) | |
| { | |
| sendFrame.mDidTX = true; | |
| mPcapCallback(&sendFrame, mPcapCallbackContext); | |
| } | |
| exit: | |
| if (error != kThreadError_None) | |
| { | |
| TransmitDoneTask(mTxFrame, false, kThreadError_Abort); | |
| } | |
| } | |
| extern "C" void otPlatRadioTransmitDone(otInstance *aInstance, RadioPacket *aPacket, bool aRxPending, | |
| ThreadError aError) | |
| { | |
| otLogFuncEntryMsg("%!otError!, aRxPending=%u", aError, aRxPending ? 1 : 0); | |
| #if OPENTHREAD_ENABLE_RAW_LINK_API | |
| if (aInstance->mLinkRaw.IsEnabled()) | |
| { | |
| aInstance->mLinkRaw.InvokeTransmitDone(aPacket, aRxPending, aError); | |
| } | |
| else | |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API | |
| { | |
| aInstance->mThreadNetif.GetMac().TransmitDoneTask(aPacket, aRxPending, aError); | |
| } | |
| otLogFuncExit(); | |
| } | |
| void Mac::TransmitDoneTask(RadioPacket *aPacket, bool aRxPending, ThreadError aError) | |
| { | |
| mMacTimer.Stop(); | |
| mCounters.mTxTotal++; | |
| Frame *packet = static_cast<Frame *>(aPacket); | |
| Address addr; | |
| packet->GetDstAddr(addr); | |
| if (addr.mShortAddress == kShortAddrBroadcast) | |
| { | |
| // Broadcast packet | |
| mCounters.mTxBroadcast++; | |
| } | |
| else | |
| { | |
| // Unicast packet | |
| mCounters.mTxUnicast++; | |
| } | |
| if (aError == kThreadError_Abort) | |
| { | |
| mCounters.mTxErrAbort++; | |
| } | |
| if (aError == kThreadError_ChannelAccessFailure) | |
| { | |
| mCounters.mTxErrCca++; | |
| } | |
| if (!RadioSupportsCsmaBackoff() && | |
| aError == kThreadError_ChannelAccessFailure && | |
| mCsmaAttempts < kMaxCSMABackoffs) | |
| { | |
| mCsmaAttempts++; | |
| StartCsmaBackoff(); | |
| ExitNow(); | |
| } | |
| mCsmaAttempts = 0; | |
| switch (mState) | |
| { | |
| case kStateTransmitData: | |
| if (aRxPending) | |
| { | |
| mReceiveTimer.Start(kDataPollTimeout); | |
| } | |
| // fall through | |
| case kStateActiveScan: | |
| case kStateTransmitBeacon: | |
| SentFrame(aError); | |
| break; | |
| default: | |
| assert(false); | |
| break; | |
| } | |
| exit: | |
| return; | |
| } | |
| void Mac::HandleMacTimer(void *aContext) | |
| { | |
| static_cast<Mac *>(aContext)->HandleMacTimer(); | |
| } | |
| void Mac::HandleMacTimer(void) | |
| { | |
| Address addr; | |
| switch (mState) | |
| { | |
| case kStateActiveScan: | |
| do | |
| { | |
| mScanChannels >>= 1; | |
| mScanChannel++; | |
| if (mScanChannels == 0 || mScanChannel > kPhyMaxChannel) | |
| { | |
| otPlatRadioReceive(GetInstance(), mChannel); | |
| otPlatRadioSetPanId(GetInstance(), mPanId); | |
| mActiveScanHandler(mScanContext, NULL); | |
| ScheduleNextTransmission(); | |
| ExitNow(); | |
| } | |
| } | |
| while ((mScanChannels & 1) == 0); | |
| StartCsmaBackoff(); | |
| break; | |
| case kStateEnergyScan: | |
| EnergyScanDone(mEnergyScanCurrentMaxRssi); | |
| break; | |
| case kStateTransmitData: | |
| otLogDebgMac(GetInstance(), "ack timer fired"); | |
| otPlatRadioReceive(GetInstance(), mChannel); | |
| mCounters.mTxTotal++; | |
| mTxFrame->GetDstAddr(addr); | |
| if (addr.mShortAddress == kShortAddrBroadcast) | |
| { | |
| // Broadcast packet | |
| mCounters.mTxBroadcast++; | |
| } | |
| else | |
| { | |
| // Unicast Packet | |
| mCounters.mTxUnicast++; | |
| } | |
| SentFrame(kThreadError_NoAck); | |
| break; | |
| default: | |
| assert(false); | |
| break; | |
| } | |
| exit: | |
| return; | |
| } | |
| void Mac::HandleReceiveTimer(void *aContext) | |
| { | |
| static_cast<Mac *>(aContext)->HandleReceiveTimer(); | |
| } | |
| void Mac::HandleReceiveTimer(void) | |
| { | |
| otLogDebgMac(GetInstance(), "data poll timeout!"); | |
| for (Receiver *receiver = mReceiveHead; receiver; receiver = receiver->mNext) | |
| { | |
| receiver->HandleDataPollTimeout(); | |
| } | |
| if (mState == kStateIdle) | |
| { | |
| NextOperation(); | |
| } | |
| } | |
| void Mac::SentFrame(ThreadError aError) | |
| { | |
| Frame &sendFrame(*mTxFrame); | |
| Sender *sender; | |
| Address dstAddr; | |
| mTransmitAttempts++; | |
| switch (aError) | |
| { | |
| case kThreadError_None: | |
| break; | |
| case kThreadError_ChannelAccessFailure: | |
| case kThreadError_Abort: | |
| case kThreadError_NoAck: | |
| sendFrame.GetDstAddr(dstAddr); | |
| switch (dstAddr.mLength) | |
| { | |
| case sizeof(ShortAddress): | |
| otLogInfoMac(GetInstance(), "Tx failed - Error: %s (%d) - SeqNum: %d - Attempt %d/%d - Dst (short): %04x", | |
| otThreadErrorToString(aError), aError, sendFrame.GetSequence(), mTransmitAttempts, | |
| sendFrame.GetMaxTxAttempts(), dstAddr.mShortAddress); | |
| break; | |
| case sizeof(ExtAddress): | |
| otLogInfoMac(GetInstance(), "Tx failed - Error: %s (%d) - SeqNum: %d - Attempt %d/%d - " | |
| "Dst: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", | |
| otThreadErrorToString(aError), aError, sendFrame.GetSequence(), mTransmitAttempts, | |
| sendFrame.GetMaxTxAttempts(), dstAddr.mExtAddress.m8[0], dstAddr.mExtAddress.m8[1], | |
| dstAddr.mExtAddress.m8[2], dstAddr.mExtAddress.m8[3], dstAddr.mExtAddress.m8[4], | |
| dstAddr.mExtAddress.m8[5], dstAddr.mExtAddress.m8[6], dstAddr.mExtAddress.m8[7]); | |
| break; | |
| default: | |
| otLogInfoMac(GetInstance(), "Tx failed - Error: %s (%d) - SeqNum: %d - Attempt %d/%d", | |
| otThreadErrorToString(aError), aError, sendFrame.GetSequence(), mTransmitAttempts, | |
| sendFrame.GetMaxTxAttempts()); | |
| break; | |
| } | |
| otDumpDebgMac(GetInstance(), "TX ERR", sendFrame.GetHeader(), 16); | |
| if (!RadioSupportsRetries() && | |
| mTransmitAttempts < sendFrame.GetMaxTxAttempts()) | |
| { | |
| StartCsmaBackoff(); | |
| mCounters.mTxRetry++; | |
| ExitNow(); | |
| } | |
| break; | |
| default: | |
| assert(false); | |
| break; | |
| } | |
| mTransmitAttempts = 0; | |
| mCsmaAttempts = 0; | |
| if (sendFrame.GetAckRequest()) | |
| { | |
| mCounters.mTxAckRequested++; | |
| if (aError == kThreadError_None) | |
| { | |
| mCounters.mTxAcked++; | |
| } | |
| } | |
| else | |
| { | |
| mCounters.mTxNoAckRequested++; | |
| } | |
| switch (mState) | |
| { | |
| case kStateActiveScan: | |
| mCounters.mTxBeaconRequest++; | |
| mMacTimer.Start(mScanDuration); | |
| break; | |
| case kStateTransmitBeacon: | |
| mCounters.mTxBeacon++; | |
| ScheduleNextTransmission(); | |
| break; | |
| case kStateTransmitData: | |
| 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); | |
| ScheduleNextTransmission(); | |
| break; | |
| default: | |
| assert(false); | |
| break; | |
| } | |
| exit: | |
| return; | |
| } | |
| ThreadError Mac::ProcessReceiveSecurity(Frame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor) | |
| { | |
| ThreadError error = kThreadError_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 = mNetif.GetKeyManager().GetKek()) != NULL, error = kThreadError_Security); | |
| extAddress = &aSrcAddr.mExtAddress; | |
| break; | |
| case Frame::kKeyIdMode1: | |
| VerifyOrExit(aNeighbor != NULL, error = kThreadError_Security); | |
| aFrame.GetKeyId(keyid); | |
| keyid--; | |
| if (keyid == (mNetif.GetKeyManager().GetCurrentKeySequence() & 0x7f)) | |
| { | |
| // same key index | |
| keySequence = mNetif.GetKeyManager().GetCurrentKeySequence(); | |
| macKey = mNetif.GetKeyManager().GetCurrentMacKey(); | |
| } | |
| else if (keyid == ((mNetif.GetKeyManager().GetCurrentKeySequence() - 1) & 0x7f)) | |
| { | |
| // previous key index | |
| keySequence = mNetif.GetKeyManager().GetCurrentKeySequence() - 1; | |
| macKey = mNetif.GetKeyManager().GetTemporaryMacKey(keySequence); | |
| } | |
| else if (keyid == ((mNetif.GetKeyManager().GetCurrentKeySequence() + 1) & 0x7f)) | |
| { | |
| // next key index | |
| keySequence = mNetif.GetKeyManager().GetCurrentKeySequence() + 1; | |
| macKey = mNetif.GetKeyManager().GetTemporaryMacKey(keySequence); | |
| } | |
| else | |
| { | |
| ExitNow(error = kThreadError_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 = kThreadError_Security); | |
| } | |
| else if (keySequence == aNeighbor->GetKeySequence()) | |
| { | |
| if ((frameCounter + 1) < aNeighbor->GetLinkFrameCounter()) | |
| { | |
| ExitNow(error = kThreadError_Security); | |
| } | |
| else if ((frameCounter + 1) == aNeighbor->GetLinkFrameCounter()) | |
| { | |
| // drop duplicated packets | |
| ExitNow(error = kThreadError_Duplicated); | |
| } | |
| } | |
| } | |
| extAddress = &aSrcAddr.mExtAddress; | |
| break; | |
| case Frame::kKeyIdMode2: | |
| macKey = sMode2Key; | |
| extAddress = static_cast<const ExtAddress *>(&sMode2ExtAddress); | |
| break; | |
| default: | |
| ExitNow(error = kThreadError_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 = kThreadError_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 > mNetif.GetKeyManager().GetCurrentKeySequence()) | |
| { | |
| mNetif.GetKeyManager().SetCurrentKeySequence(keySequence); | |
| } | |
| } | |
| aFrame.SetSecurityValid(true); | |
| exit: | |
| return error; | |
| } | |
| extern "C" void otPlatRadioReceiveDone(otInstance *aInstance, RadioPacket *aFrame, ThreadError 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, ThreadError aError) | |
| { | |
| Address srcaddr; | |
| Address dstaddr; | |
| PanId panid; | |
| Neighbor *neighbor; | |
| otMacWhitelistEntry *whitelistEntry; | |
| otMacBlacklistEntry *blacklistEntry; | |
| int8_t rssi; | |
| bool receive = false; | |
| uint8_t commandId; | |
| ThreadError error = aError; | |
| mCounters.mRxTotal++; | |
| VerifyOrExit(error == kThreadError_None); | |
| VerifyOrExit(aFrame != NULL, error = kThreadError_NoFrameReceived); | |
| 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 = mNetif.GetMle().GetNeighbor(srcaddr); | |
| switch (srcaddr.mLength) | |
| { | |
| case 0: | |
| break; | |
| case sizeof(ShortAddress): | |
| otLogDebgMac(GetInstance(), "Received from short address %x", srcaddr.mShortAddress); | |
| if (neighbor == NULL) | |
| { | |
| otLogDebgMac(GetInstance(), "drop not neighbor"); | |
| ExitNow(error = kThreadError_UnknownNeighbor); | |
| } | |
| srcaddr.mLength = sizeof(srcaddr.mExtAddress); | |
| srcaddr.mExtAddress = neighbor->GetExtAddress(); | |
| break; | |
| case sizeof(ExtAddress): | |
| break; | |
| default: | |
| ExitNow(error = kThreadError_InvalidSourceAddress); | |
| } | |
| // Duplicate Address Protection | |
| if (memcmp(&srcaddr.mExtAddress, &mExtAddress, sizeof(srcaddr.mExtAddress)) == 0) | |
| { | |
| otLogDebgMac(GetInstance(), "duplicate address received"); | |
| ExitNow(error = kThreadError_InvalidSourceAddress); | |
| } | |
| // Source Whitelist Processing | |
| if (srcaddr.mLength != 0 && mWhitelist.IsEnabled()) | |
| { | |
| VerifyOrExit((whitelistEntry = mWhitelist.Find(srcaddr.mExtAddress)) != NULL, error = kThreadError_WhitelistFiltered); | |
| if (mWhitelist.GetFixedRssi(*whitelistEntry, rssi) == kThreadError_None) | |
| { | |
| aFrame->mPower = rssi; | |
| } | |
| } | |
| // Source Blacklist Processing | |
| if (srcaddr.mLength != 0 && mBlacklist.IsEnabled()) | |
| { | |
| VerifyOrExit((blacklistEntry = mBlacklist.Find(srcaddr.mExtAddress)) == NULL, error = kThreadError_BlacklistFiltered); | |
| } | |
| // 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 = kThreadError_DestinationAddressFiltered); | |
| break; | |
| case sizeof(ExtAddress): | |
| aFrame->GetDstPanId(panid); | |
| VerifyOrExit(panid == mPanId && | |
| memcmp(&dstaddr.mExtAddress, &mExtAddress, sizeof(dstaddr.mExtAddress)) == 0, | |
| error = kThreadError_DestinationAddressFiltered); | |
| break; | |
| } | |
| // Increment counters | |
| if (dstaddr.mShortAddress == kShortAddrBroadcast) | |
| { | |
| // Broadcast packet | |
| mCounters.mRxBroadcast++; | |
| } | |
| else | |
| { | |
| // Unicast packet | |
| mCounters.mRxUnicast++; | |
| } | |
| // Security Processing | |
| SuccessOrExit(error = ProcessReceiveSecurity(*aFrame, srcaddr, neighbor)); | |
| if (neighbor != NULL) | |
| { | |
| neighbor->GetLinkInfo().AddRss(mNoiseFloor, 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 = kThreadError_Drop); | |
| VerifyOrExit(aFrame->GetCommandId(commandId) == kThreadError_None, error = kThreadError_Drop); | |
| VerifyOrExit(commandId == Frame::kMacCmdDataRequest, error = kThreadError_Drop); | |
| break; | |
| default: | |
| ExitNow(error = kThreadError_UnknownNeighbor); | |
| } | |
| } | |
| } | |
| switch (mState) | |
| { | |
| case kStateActiveScan: | |
| if (aFrame->GetType() == Frame::kFcfFrameBeacon) | |
| { | |
| mCounters.mRxBeacon++; | |
| mActiveScanHandler(mScanContext, aFrame); | |
| } | |
| else | |
| { | |
| mCounters.mRxOther++; | |
| } | |
| break; | |
| default: | |
| if (!mRxOnWhenIdle && dstaddr.mLength != 0) | |
| { | |
| mReceiveTimer.Stop(); | |
| otPlatRadioSleep(GetInstance()); | |
| } | |
| switch (aFrame->GetType()) | |
| { | |
| case Frame::kFcfFrameMacCmd: | |
| if (HandleMacCommand(*aFrame) == kThreadError_Drop) | |
| { | |
| ExitNow(error = kThreadError_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 (error != kThreadError_None) | |
| { | |
| otLogDebgMacErr(GetInstance(), error, "Dropping received frame"); | |
| switch (error) | |
| { | |
| case kThreadError_Security: | |
| mCounters.mRxErrSec++; | |
| break; | |
| case kThreadError_FcsErr: | |
| mCounters.mRxErrFcs++; | |
| break; | |
| case kThreadError_NoFrameReceived: | |
| mCounters.mRxErrNoFrame++; | |
| break; | |
| case kThreadError_UnknownNeighbor: | |
| mCounters.mRxErrUnknownNeighbor++; | |
| break; | |
| case kThreadError_InvalidSourceAddress: | |
| mCounters.mRxErrInvalidSrcAddr++; | |
| break; | |
| case kThreadError_WhitelistFiltered: | |
| mCounters.mRxWhitelistFiltered++; | |
| break; | |
| case kThreadError_DestinationAddressFiltered: | |
| mCounters.mRxDestAddrFiltered++; | |
| break; | |
| case kThreadError_Duplicated: | |
| mCounters.mRxDuplicated++; | |
| break; | |
| default: | |
| mCounters.mRxErrOther++; | |
| break; | |
| } | |
| } | |
| } | |
| ThreadError Mac::HandleMacCommand(Frame &aFrame) | |
| { | |
| ThreadError error = kThreadError_None; | |
| uint8_t commandId; | |
| aFrame.GetCommandId(commandId); | |
| switch (commandId) | |
| { | |
| case Frame::kMacCmdBeaconRequest: | |
| mCounters.mRxBeaconRequest++; | |
| otLogDebgMac(GetInstance(), "Received Beacon Request"); | |
| if (mBeaconsEnabled) | |
| { | |
| mTransmitBeacon = true; | |
| if (mState == kStateIdle) | |
| { | |
| mState = kStateTransmitBeacon; | |
| mTransmitBeacon = false; | |
| StartCsmaBackoff(); | |
| } | |
| } | |
| ExitNow(error = kThreadError_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); | |
| if (mState == kStateIdle) | |
| { | |
| NextOperation(); | |
| } | |
| } | |
| bool Mac::RadioSupportsCsmaBackoff(void) | |
| { | |
| /* Check either of the following conditions: | |
| * 1) Radio provides the CSMA backoff capability (i.e., `kRadioCapsCsmaBackOff` bit is set) or; | |
| * 2) It provides `kRadioCapsTransmitRetries` which indicates support for MAC retries along with CSMA backoff. | |
| */ | |
| return (otPlatRadioGetCaps(GetInstance()) & (kRadioCapsTransmitRetries | kRadioCapsCsmaBackOff)) != 0; | |
| } | |
| bool Mac::RadioSupportsRetries(void) | |
| { | |
| return (otPlatRadioGetCaps(GetInstance()) & kRadioCapsTransmitRetries) != 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)); | |
| } | |
| } // namespace Mac | |
| } // namespace Thread |