[mac-frame] update GenerateEnhAck() to use InitMacHeader() (#9338)

This commit updates the `GenerateEnhAck()` method to add checks to
validate the received frame before preparing the ACK. These checks
are added as a safeguard in case the caller (radio platform
implementation) does not validate the received frame before calling
this method to generate the ACK.

Specifically, the checks verify that the received frame is using the
2015 version, has the "Ack Request" flag, has a valid source address
(which is used as the destination in the generated ACK), and has a
valid destination address that is not broadcast. The checks also
verify that if the received frame is secured, it uses security level
`kSecurityEncMic32`.

The commit also simplifies the code by using `InitMacHeader()` to
prepare the header and addresses. Enhanced ACK frames always have a
destination address and no source address (to keep the frame
shorter). If the received frame has a source PAN ID, it is used in
the ACK frame. If it does not, the code checks if the received frame
has a destination PAN ID and uses that in the ACK frame.
This commit is contained in:
Abtin Keshavarzian
2023-08-14 21:45:38 -07:00
committed by GitHub
parent 4d494a864f
commit 3b291108a9
3 changed files with 92 additions and 101 deletions
+76 -93
View File
@@ -239,6 +239,8 @@ void Frame::InitMacHeader(Type aType,
uint16_t Frame::GetFrameControlField(void) const { return ReadUint16(mPsdu); }
void Frame::SetFrameControlField(uint16_t aFcf) { WriteUint16(aFcf, mPsdu); }
Error Frame::ValidatePsdu(void) const
{
Error error = kErrorNone;
@@ -275,6 +277,22 @@ void Frame::SetFramePending(bool aFramePending)
}
}
void Frame::SetIePresent(bool aIePresent)
{
uint16_t fcf = GetFrameControlField();
if (aIePresent)
{
fcf |= kFcfIePresent;
}
else
{
fcf &= ~kFcfIePresent;
}
SetFrameControlField(fcf);
}
uint8_t Frame::FindDstPanIdIndex(void) const
{
uint8_t index;
@@ -1090,7 +1108,7 @@ Error Frame::InitIeHeaderAt(uint8_t &aIndex, uint8_t ieId, uint8_t ieContentSize
{
Error error = kErrorNone;
WriteUint16(GetFrameControlField() | kFcfIePresent, mPsdu);
SetIePresent(true);
if (aIndex == 0)
{
@@ -1386,115 +1404,80 @@ void TxFrame::GenerateImmAck(const RxFrame &aFrame, bool aIsFramePending)
}
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
Error TxFrame::GenerateEnhAck(const RxFrame &aFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength)
Error TxFrame::GenerateEnhAck(const RxFrame &aRxFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength)
{
Error error = kErrorNone;
Error error = kErrorNone;
Address address;
PanId panId;
Addresses addrs;
PanIds panIds;
uint8_t securityLevel = kSecurityNone;
uint8_t keyIdMode = kKeyIdMode0;
uint16_t fcf;
Address address;
PanId panId;
uint8_t footerLength;
uint8_t securityControlField;
uint8_t keyId;
// Validate the received frame.
fcf = static_cast<uint16_t>(kTypeAck) | static_cast<uint16_t>(kVersion2015) | kFcfSrcAddrNone;
VerifyOrExit(aRxFrame.IsVersion2015(), error = kErrorParse);
VerifyOrExit(aRxFrame.GetAckRequest(), error = kErrorParse);
mChannel = aFrame.mChannel;
// Check `aRxFrame` has a valid destination address. The ack frame
// will not use this as its source though and will always use no
// source address.
SuccessOrExit(error = aRxFrame.GetDstAddr(address));
VerifyOrExit(!address.IsNone() && !address.IsBroadcast(), error = kErrorParse);
// Check `aRxFrame` has a valid source, which is then used as
// ack frames destination.
SuccessOrExit(error = aRxFrame.GetSrcAddr(addrs.mDestination));
VerifyOrExit(!addrs.mDestination.IsNone(), error = kErrorParse);
if (aRxFrame.GetSecurityEnabled())
{
SuccessOrExit(error = aRxFrame.GetSecurityLevel(securityLevel));
VerifyOrExit(securityLevel == kSecurityEncMic32, error = kErrorParse);
SuccessOrExit(error = aRxFrame.GetKeyIdMode(keyIdMode));
}
if (aRxFrame.IsSrcPanIdPresent())
{
SuccessOrExit(error = aRxFrame.GetSrcPanId(panId));
panIds.SetDestination(panId);
}
else if (aRxFrame.IsDstPanIdPresent())
{
SuccessOrExit(error = aRxFrame.GetDstPanId(panId));
panIds.SetDestination(panId);
}
// Prepare the ack frame
mChannel = aRxFrame.mChannel;
memset(&mInfo.mTxInfo, 0, sizeof(mInfo.mTxInfo));
// Set frame control field
if (aIsFramePending)
InitMacHeader(kTypeAck, kVersion2015, addrs, panIds, static_cast<SecurityLevel>(securityLevel),
static_cast<KeyIdMode>(keyIdMode));
SetFramePending(aIsFramePending);
SetIePresent(aIeLength != 0);
SetSequence(aRxFrame.GetSequence());
if (aRxFrame.GetSecurityEnabled())
{
fcf |= kFcfFramePending;
}
uint8_t keyId;
if (aFrame.GetSecurityEnabled())
{
fcf |= kFcfSecurityEnabled;
}
if (aFrame.IsPanIdCompressed())
{
fcf |= kFcfPanidCompression;
}
// Destination address mode
if ((aFrame.GetFrameControlField() & kFcfSrcAddrMask) == kFcfSrcAddrExt)
{
fcf |= kFcfDstAddrExt;
}
else if ((aFrame.GetFrameControlField() & kFcfSrcAddrMask) == kFcfSrcAddrShort)
{
fcf |= kFcfDstAddrShort;
}
else
{
fcf |= kFcfDstAddrNone;
}
if (aIeLength > 0)
{
fcf |= kFcfIePresent;
}
WriteUint16(fcf, mPsdu);
// Set sequence number
mPsdu[kSequenceIndex] = aFrame.GetSequence();
if (IsDstPanIdPresent())
{
// Set address field
if (aFrame.IsSrcPanIdPresent())
{
SuccessOrExit(error = aFrame.GetSrcPanId(panId));
}
else if (aFrame.IsDstPanIdPresent())
{
SuccessOrExit(error = aFrame.GetDstPanId(panId));
}
else
{
ExitNow(error = kErrorParse);
}
SetDstPanId(panId);
}
if (aFrame.IsSrcAddrPresent())
{
SuccessOrExit(error = aFrame.GetSrcAddr(address));
SetDstAddr(address);
}
// At this time the length of ACK hasn't been determined, set it to
// `kMaxPsduSize` to call methods that check frame length
mLength = kMaxPsduSize;
// Set security header
if (aFrame.GetSecurityEnabled())
{
SuccessOrExit(error = aFrame.GetSecurityControlField(securityControlField));
SuccessOrExit(error = aFrame.GetKeyId(keyId));
VerifyOrExit((securityControlField & kSecLevelMask) == kSecurityEncMic32, error = kErrorParse);
SetSecurityControlField(securityControlField);
SuccessOrExit(error = aRxFrame.GetKeyId(keyId));
SetKeyId(keyId);
}
// Set header IE
if (aIeLength > 0)
{
OT_ASSERT(aIeData != nullptr);
memcpy(&mPsdu[FindHeaderIeIndex()], aIeData, aIeLength);
mLength += aIeLength;
}
// Set frame length
footerLength = GetFooterLength();
OT_ASSERT(footerLength != kInvalidIndex);
mLength = SkipSecurityHeaderIndex() + aIeLength + footerLength;
exit:
return error;
}
+12 -5
View File
@@ -504,6 +504,14 @@ public:
*/
bool IsIePresent(void) const { return (GetFrameControlField() & kFcfIePresent) != 0; }
/**
* Sets the IE Present bit.
*
* @param[in] aIePresent The IE Present bit.
*
*/
void SetIePresent(bool aIePresent);
/**
* Returns the Sequence Number value.
*
@@ -1122,6 +1130,7 @@ protected:
static constexpr uint8_t kMaxPsduSize = kInvalidSize - 1;
static constexpr uint8_t kSequenceIndex = kFcfSize;
void SetFrameControlField(uint16_t aFcf);
uint8_t FindDstPanIdIndex(void) const;
uint8_t FindDstAddrIndex(void) const;
uint8_t FindSrcPanIdIndex(void) const;
@@ -1148,8 +1157,6 @@ protected:
static uint8_t CalculateAddrFieldSize(uint16_t aFcf);
static uint8_t CalculateSecurityHeaderSize(uint8_t aSecurityControl);
static uint8_t CalculateMicSize(uint8_t aSecurityControl);
public:
};
/**
@@ -1502,16 +1509,16 @@ public:
/**
* Generate Enh-Ack in this frame object.
*
* @param[in] aFrame A reference to the frame received.
* @param[in] aRxFrame A reference to the received frame.
* @param[in] aIsFramePending Value of the ACK's frame pending bit.
* @param[in] aIeData A pointer to the IE data portion of the ACK to be sent.
* @param[in] aIeLength The length of IE data portion of the ACK to be sent.
*
* @retval kErrorNone Successfully generated Enh Ack.
* @retval kErrorParse @p aFrame has incorrect format.
* @retval kErrorParse @p aRxFrame has incorrect format.
*
*/
Error GenerateEnhAck(const RxFrame &aFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength);
Error GenerateEnhAck(const RxFrame &aRxFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength);
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
/**
+4 -3
View File
@@ -747,13 +747,14 @@ void TestMacFrameAckGeneration(void)
uint8_t ie_data[6] = {0x04, 0x0d, 0x21, 0x0c, 0x35, 0x0c};
Mac::CslIe *csl;
IgnoreError(ackFrame.GenerateEnhAck(receivedFrame, false, ie_data, sizeof(ie_data)));
SuccessOrQuit(ackFrame.GenerateEnhAck(receivedFrame, false, ie_data, sizeof(ie_data)));
csl = reinterpret_cast<Mac::CslIe *>(ackFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) + sizeof(Mac::HeaderIe));
VerifyOrQuit(ackFrame.mLength == 23);
VerifyOrQuit(ackFrame.mLength == 25);
VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kTypeAck);
VerifyOrQuit(ackFrame.GetSecurityEnabled());
VerifyOrQuit(ackFrame.IsIePresent());
VerifyOrQuit(!ackFrame.IsDstPanIdPresent());
VerifyOrQuit(ackFrame.IsDstPanIdPresent());
VerifyOrQuit(ackFrame.IsDstAddrPresent());
VerifyOrQuit(!ackFrame.IsSrcAddrPresent());
VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2015);