Files
Yakun Xu 90224407f0 [radio] fix alternate short address initialization and matching (#13700)
When a router-eligible Child transitions to Router (e.g. during
TestCoapObserve when Leader notifies an upgraded child at its former
child RLOC16), OpenThread calls otPlatRadioSetAlternateShortAddress(),
which stores the previous short address in mRadioContext.mAlternateShortAddress.

Fix alternate short address handling consistently across Nexus,
simulation radio, and MAC frame utils:
- Advertise OT_RADIO_CAPS_ALT_SHORT_ADDR in both Nexus otPlatRadioGetCaps()
  and simulation gRadioCaps.
- Initialize mCslShortAddress, mShortAddress, and mAlternateShortAddress
  in otRadioContext to OT_RADIO_INVALID_SHORT_ADDR in both
  Nexus::Radio::Reset() and simulation platformRadioInit().
- Drop duplicate Radio::mShortAddress and Radio::mExtAddress from
  Nexus::Radio and use mRadioContext consistently.
- Simplify Nexus::Radio::Radio(void) to call Reset().
- Guard against Mac::kShortAddrInvalid and match both short addresses in
  Nexus::Radio::Matches() and otMacFrameDoesAddrMatchAny().
2026-10-09 23:21:19 -07:00

508 lines
16 KiB
C++

/*
* Copyright (c) 2019, 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.
*/
#include "mac_frame.h"
#include <assert.h>
#include <openthread/platform/radio.h>
#include "common/code_utils.hpp"
#include "mac/mac_frame.hpp"
using namespace ot;
//---------------------------------------------------------------------------------------------------------------------
// Helpers
static inline Error ParseAddrFields(const otRadioFrame *aFrame, Mac::Frame::ParseInfo &aFrameInfo)
{
return aFrameInfo.ParseFrom(*static_cast<const Mac::Frame *>(aFrame), Mac::Frame::kParseAddrFields);
}
static inline Error ParseSecurityHeader(const otRadioFrame *aFrame, Mac::TxFrame::ParseInfo &aFrameInfo)
{
return aFrameInfo.ParseFrom(*static_cast<const Mac::TxFrame *>(aFrame), Mac::Frame::kParseSecurityHeader);
}
static inline Error ParseFully(const otRadioFrame *aFrame, Mac::Frame::ParseInfo &aFrameInfo)
{
return aFrameInfo.ParseFrom(*static_cast<const Mac::Frame *>(aFrame), Mac::Frame::kParseFully);
}
static bool IsFrameOfType(const otRadioFrame *aFrame, Mac::Frame::Type aType)
{
bool matches = false;
Mac::Frame::ParseInfo frameInfo;
SuccessOrExit(ParseAddrFields(aFrame, frameInfo));
matches = (frameInfo.mType == aType);
exit:
return matches;
}
static bool HasFrameKeyIdMode(const otRadioFrame *aFrame, Mac::Frame::KeyIdMode aKeyIdMode)
{
bool matches = false;
Mac::TxFrame::ParseInfo frameInfo;
SuccessOrExit(ParseSecurityHeader(aFrame, frameInfo));
VerifyOrExit(frameInfo.mKeyIdMode == aKeyIdMode);
matches = true;
exit:
return matches;
}
//---------------------------------------------------------------------------------------------------------------------
bool otMacFrameDoesAddrMatch(const otRadioFrame *aFrame,
otPanId aPanId,
otShortAddress aShortAddress,
const otExtAddress *aExtAddress)
{
return otMacFrameDoesAddrMatchAny(aFrame, aPanId, aShortAddress, Mac::kShortAddrInvalid, aExtAddress);
}
bool otMacFrameDoesAddrMatchAny(const otRadioFrame *aFrame,
otPanId aPanId,
otShortAddress aShortAddress,
otShortAddress aAltShortAddress,
const otExtAddress *aExtAddress)
{
bool rval = true;
Mac::Frame::ParseInfo frameInfo;
if (ParseAddrFields(aFrame, frameInfo) != kErrorNone)
{
rval = false;
ExitNow();
}
switch (frameInfo.mAddrs.mDestination.GetType())
{
case Mac::Address::kTypeShort:
{
Mac::ShortAddress dstAddr = frameInfo.mAddrs.mDestination.GetShort();
VerifyOrExit(dstAddr != Mac::kShortAddrInvalid, rval = false);
VerifyOrExit(dstAddr == Mac::kShortAddrBroadcast || dstAddr == aShortAddress || dstAddr == aAltShortAddress,
rval = false);
break;
}
case Mac::Address::kTypeExtended:
VerifyOrExit(frameInfo.mAddrs.mDestination.GetExtended() == *static_cast<const Mac::ExtAddress *>(aExtAddress),
rval = false);
break;
case Mac::Address::kTypeNone:
break;
}
VerifyOrExit(frameInfo.mPanIds.IsDestinationPresent());
VerifyOrExit(frameInfo.mPanIds.GetDestination() == Mac::kPanIdBroadcast ||
frameInfo.mPanIds.GetDestination() == aPanId,
rval = false);
exit:
return rval;
}
bool otMacFrameIsAck(const otRadioFrame *aFrame) { return IsFrameOfType(aFrame, Mac::Frame::kTypeAck); }
bool otMacFrameIsData(const otRadioFrame *aFrame) { return IsFrameOfType(aFrame, Mac::Frame::kTypeData); }
bool otMacFrameIsCommand(const otRadioFrame *aFrame) { return IsFrameOfType(aFrame, Mac::Frame::kTypeMacCmd); }
bool otMacFrameIsDataRequest(const otRadioFrame *aFrame)
{
bool matches = false;
Mac::Frame::ParseInfo frameInfo;
SuccessOrExit(ParseFully(aFrame, frameInfo));
VerifyOrExit(frameInfo.mType == Mac::Frame::kTypeMacCmd);
VerifyOrExit(frameInfo.mCommandId == Mac::Frame::kMacCmdDataRequest);
matches = true;
exit:
return matches;
}
bool otMacFrameIsAckRequested(const otRadioFrame *aFrame)
{
Mac::Frame::ParseInfo frameInfo;
IgnoreError(ParseAddrFields(aFrame, frameInfo));
return frameInfo.mIsAckRequest;
}
static void GetOtMacAddress(const Mac::Address &aInAddress, otMacAddress *aOutAddress)
{
switch (aInAddress.GetType())
{
case Mac::Address::kTypeNone:
aOutAddress->mType = OT_MAC_ADDRESS_TYPE_NONE;
break;
case Mac::Address::kTypeShort:
aOutAddress->mType = OT_MAC_ADDRESS_TYPE_SHORT;
aOutAddress->mAddress.mShortAddress = aInAddress.GetShort();
break;
case Mac::Address::kTypeExtended:
aOutAddress->mType = OT_MAC_ADDRESS_TYPE_EXTENDED;
aOutAddress->mAddress.mExtAddress = aInAddress.GetExtended();
break;
}
}
otError otMacFrameGetSrcAddr(const otRadioFrame *aFrame, otMacAddress *aMacAddress)
{
Error error;
Mac::Frame::ParseInfo frameInfo;
SuccessOrExit(error = ParseAddrFields(aFrame, frameInfo));
GetOtMacAddress(frameInfo.mAddrs.mSource, aMacAddress);
exit:
return error;
}
otError otMacFrameGetDstAddr(const otRadioFrame *aFrame, otMacAddress *aMacAddress)
{
Error error;
Mac::Frame::ParseInfo frameInfo;
SuccessOrExit(error = ParseAddrFields(aFrame, frameInfo));
GetOtMacAddress(frameInfo.mAddrs.mDestination, aMacAddress);
exit:
return error;
}
otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence)
{
Error error;
Mac::Frame::ParseInfo frameInfo;
SuccessOrExit(error = ParseAddrFields(aFrame, frameInfo));
VerifyOrExit(frameInfo.mIsSeqNumPresent, error = kErrorParse);
*aSequence = frameInfo.mSequenceNum;
exit:
return error;
}
void otMacFrameProcessTransmitAesCcm(otRadioFrame *aFrame, const otExtAddress *aExtAddress)
{
Mac::TxFrame::ParseInfo frameInfo;
IgnoreError(ParseFully(aFrame, frameInfo));
frameInfo.ProcessTransmitAesCcm(AsCoreType(aExtAddress));
}
bool otMacFrameIsVersion2015(const otRadioFrame *aFrame)
{
Mac::Frame::ParseInfo frameInfo;
IgnoreError(ParseAddrFields(aFrame, frameInfo));
return frameInfo.mVersion == Mac::Frame::kVersion2015;
}
void otMacFrameGenerateImmAck(const otRadioFrame *aFrame, bool aIsFramePending, otRadioFrame *aAckFrame)
{
assert(aFrame != nullptr && aAckFrame != nullptr);
static_cast<Mac::TxFrame *>(aAckFrame)->GenerateImmAck(*static_cast<const Mac::RxFrame *>(aFrame), aIsFramePending);
}
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
otError otMacFrameGenerateEnhAck(const otRadioFrame *aFrame,
bool aIsFramePending,
const uint8_t *aIeData,
uint8_t aIeLength,
otRadioFrame *aAckFrame)
{
assert(aFrame != nullptr && aAckFrame != nullptr);
return static_cast<Mac::TxFrame *>(aAckFrame)->GenerateEnhAck(*static_cast<const Mac::RxFrame *>(aFrame),
aIsFramePending, aIeData, aIeLength);
}
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void otMacFrameSetCslIe(otRadioFrame *aFrame, uint16_t aCslPeriod, uint16_t aCslPhase)
{
static_cast<Mac::Frame *>(aFrame)->UpdateCslIe(aCslPeriod, aCslPhase);
}
#endif
bool otMacFrameIsSecurityEnabled(otRadioFrame *aFrame)
{
Mac::Frame::ParseInfo frameInfo;
IgnoreError(ParseAddrFields(aFrame, frameInfo));
return frameInfo.mIsSecurityEnabled;
}
bool otMacFrameIsKeyIdMode1(otRadioFrame *aFrame) { return HasFrameKeyIdMode(aFrame, Mac::Frame::kKeyIdMode1); }
bool otMacFrameIsKeyIdMode2(otRadioFrame *aFrame) { return HasFrameKeyIdMode(aFrame, Mac::Frame::kKeyIdMode2); }
uint8_t otMacFrameGetKeyId(otRadioFrame *aFrame)
{
uint8_t keyIndex = 0;
Mac::TxFrame::ParseInfo frameInfo;
SuccessOrExit(ParseSecurityHeader(aFrame, frameInfo));
keyIndex = frameInfo.mKeyIndex;
exit:
return keyIndex;
}
void otMacFrameSetKeyId(otRadioFrame *aFrame, uint8_t aKeyId)
{
Mac::TxFrame::ParseInfo frameInfo;
IgnoreError(ParseSecurityHeader(aFrame, frameInfo));
frameInfo.WriteKeyIndex(aKeyId);
}
uint32_t otMacFrameGetFrameCounter(otRadioFrame *aFrame)
{
uint32_t frameCounter = UINT32_MAX;
Mac::TxFrame::ParseInfo frameInfo;
SuccessOrExit(ParseSecurityHeader(aFrame, frameInfo));
frameCounter = frameInfo.mFrameCounter;
exit:
return frameCounter;
}
void otMacFrameSetFrameCounter(otRadioFrame *aFrame, uint32_t aFrameCounter)
{
Mac::TxFrame::ParseInfo frameInfo;
IgnoreError(ParseSecurityHeader(aFrame, frameInfo));
frameInfo.WriteFrameCounter(aFrameCounter);
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
uint8_t otMacFrameGenerateCslIeTemplate(uint8_t *aDest)
{
assert(aDest != nullptr);
reinterpret_cast<Mac::CslIe *>(aDest)->Init();
return sizeof(Mac::CslIe);
}
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
uint8_t otMacFrameGenerateEnhAckProbingIe(uint8_t *aDest, const uint8_t *aIeData, uint8_t aIeDataLength)
{
Mac::LinkMetricsProbingIe *probingIe = reinterpret_cast<Mac::LinkMetricsProbingIe *>(aDest);
assert(aDest != nullptr);
probingIe->Init(aIeDataLength);
if (aIeData != nullptr)
{
probingIe->WriteMetricsDataFrom(aIeData);
}
return probingIe->GetSize();
}
void otMacFrameSetEnhAckProbingIe(otRadioFrame *aFrame, const uint8_t *aData, uint8_t aDataLen)
{
assert(aFrame != nullptr && aData != nullptr);
reinterpret_cast<Mac::Frame *>(aFrame)->UpdateEnhAckProbingIe(aData, aDataLen);
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static uint16_t ComputeCslPhase(uint32_t aRadioTime, otRadioContext *aRadioContext)
{
return (aRadioContext->mCslSampleTime - aRadioTime) % (aRadioContext->mCslPeriod * OT_US_PER_TEN_SYMBOLS) /
OT_US_PER_TEN_SYMBOLS;
}
#endif
otError otMacFrameProcessTransmitSecurity(otRadioFrame *aFrame, otRadioContext *aRadioContext)
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
otMacKeyMaterial *key = nullptr;
uint8_t keyId;
uint32_t frameCounter;
bool processKeyId;
processKeyId = otMacFrameIsKeyIdMode1(aFrame);
VerifyOrExit(otMacFrameIsSecurityEnabled(aFrame) && processKeyId && !aFrame->mInfo.mTxInfo.mIsSecurityProcessed);
if (otMacFrameIsAck(aFrame))
{
keyId = otMacFrameGetKeyId(aFrame);
VerifyOrExit(keyId != 0, error = OT_ERROR_FAILED);
if (keyId == aRadioContext->mKeyId)
{
key = &aRadioContext->mCurrKey;
frameCounter = aRadioContext->mMacFrameCounter++;
}
else if (keyId == aRadioContext->mKeyId - 1)
{
key = &aRadioContext->mPrevKey;
frameCounter = aRadioContext->mPrevMacFrameCounter++;
}
else if (keyId == aRadioContext->mKeyId + 1)
{
key = &aRadioContext->mNextKey;
frameCounter = 0;
}
else
{
ExitNow(error = OT_ERROR_SECURITY);
}
}
else if (!aFrame->mInfo.mTxInfo.mIsHeaderUpdated)
{
key = &aRadioContext->mCurrKey;
keyId = aRadioContext->mKeyId;
frameCounter = aRadioContext->mMacFrameCounter++;
}
if (key != nullptr)
{
aFrame->mInfo.mTxInfo.mAesKey = key;
otMacFrameSetKeyId(aFrame, keyId);
otMacFrameSetFrameCounter(aFrame, frameCounter);
aFrame->mInfo.mTxInfo.mIsHeaderUpdated = true;
}
#else
VerifyOrExit(!aFrame->mInfo.mTxInfo.mIsSecurityProcessed);
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
otMacFrameProcessTransmitAesCcm(aFrame, &aRadioContext->mExtAddress);
exit:
return error;
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
void otMacFrameUpdateTimeIe(otRadioFrame *aFrame, uint64_t aRadioTime, otRadioContext *aRadioContext)
{
uint8_t *timeIe;
uint64_t time;
OT_UNUSED_VARIABLE(aRadioContext);
VerifyOrExit((aFrame->mInfo.mTxInfo.mIeInfo != nullptr) && (aFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0));
timeIe = aFrame->mPsdu + aFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset;
time = aRadioTime + aFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset;
*timeIe = aFrame->mInfo.mTxInfo.mIeInfo->mTimeSyncSeq;
*(++timeIe) = static_cast<uint8_t>(time & 0xff);
for (uint8_t i = 1; i < sizeof(uint64_t); i++)
{
time = time >> 8;
*(++timeIe) = static_cast<uint8_t>(time & 0xff);
}
exit:
return;
}
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
otError otMacFrameProcessTxSfd(otRadioFrame *aFrame, uint64_t aRadioTime, otRadioContext *aRadioContext)
{
otError error = OT_ERROR_NONE;
aFrame->mInfo.mTxInfo.mTimestamp = aRadioTime;
VerifyOrExit(!otMacFrameIsSecurityEnabled(aFrame) || !aFrame->mInfo.mTxInfo.mIsSecurityProcessed);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
{
Mac::Frame::ParseInfo frameInfo;
if ((ParseFully(aFrame, frameInfo) == kErrorNone) && frameInfo.Has<Mac::CslIe>())
{
otMacFrameSetCslIe(aFrame, aRadioContext->mCslPeriod,
ComputeCslPhase(static_cast<uint32_t>(aRadioTime), aRadioContext));
}
}
#endif
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
otMacFrameUpdateTimeIe(aFrame, aRadioTime, aRadioContext);
#endif
error = otMacFrameProcessTransmitSecurity(aFrame, aRadioContext);
exit:
return error;
}
bool otMacFrameSrcAddrMatchCslReceiverPeer(const otRadioFrame *aFrame, const otRadioContext *aRadioContext)
{
bool matches = false;
Mac::Frame::ParseInfo frameInfo;
SuccessOrExit(ParseAddrFields(aFrame, frameInfo));
switch (frameInfo.mAddrs.mSource.GetType())
{
case Mac::Address::kTypeShort:
VerifyOrExit(aRadioContext->mCslShortAddress != Mac::kShortAddrBroadcast &&
aRadioContext->mCslShortAddress != Mac::kShortAddrInvalid);
VerifyOrExit(frameInfo.mAddrs.mSource.GetShort() == aRadioContext->mCslShortAddress);
matches = true;
break;
case Mac::Address::kTypeExtended:
VerifyOrExit(frameInfo.mAddrs.mSource.GetExtended() == AsCoreType(&aRadioContext->mCslExtAddress));
matches = true;
break;
case Mac::Address::kTypeNone:
matches = false;
break;
}
exit:
return matches;
}