Files
openthread/tests/unit/test_mac_frame.cpp
T
Abtin Keshavarzian a56a79bcb7 [mac-frame] remove Multipurpose frame format support (#13281)
This commit removes the Multipurpose (MP) frame format support
(`OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME`) and associated
wake-up frame handling.

Multipurpose frame support was originally added as a provisional and
experimental solution for the Wakeup Coordinator / Wake-up End Device
(WED) wake mechanism. However, updated Thread specification designs
decided against using the Multipurpose frame format for wake frames.
Removing this now obsolete MP frame logic cleans up and simplifies
MAC frame parsing and generation ahead of implementing the updated
wake mechanism.

Key changes:
- Removes `OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME` from `mac.h` and
  adds an `#error` check in `openthread-core-config-check.h`.
- Simplifies `Mac::Frame` control field (FCF) parsing and helper methods
  by removing MP-specific frame handling and template helpers.
- Replaces legacy MP wake-up frame helpers in `mac_frame.cpp` with
  temporary placeholder stubs.
- Cleans up unit tests in `test_mac_frame.cpp` related to MP frame
  format.
- Removes the now obsolete `v1_5-cli-p2p-link.exp` test.
2026-07-03 12:53:32 -07:00

797 lines
32 KiB
C++

/*
* 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.
*/
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "mac/mac.hpp"
#include "mac/mac_frame.hpp"
#include "radio/radio.hpp"
#include "test_platform.h"
#include "test_util.hpp"
namespace ot {
bool CompareReversed(const uint8_t *aFirst, const uint8_t *aSecond, uint16_t aLength)
{
bool matches = true;
for (uint16_t i = 0; i < aLength; i++)
{
if (aFirst[i] != aSecond[aLength - 1 - i])
{
matches = false;
break;
}
}
return matches;
}
bool CompareAddresses(const Mac::Address &aFirst, const Mac::Address &aSecond)
{
bool matches = false;
VerifyOrExit(aFirst.GetType() == aSecond.GetType());
switch (aFirst.GetType())
{
case Mac::Address::kTypeNone:
break;
case Mac::Address::kTypeShort:
VerifyOrExit(aFirst.GetShort() == aSecond.GetShort());
break;
case Mac::Address::kTypeExtended:
VerifyOrExit(aFirst.GetExtended() == aSecond.GetExtended());
break;
}
matches = true;
exit:
return matches;
}
void TestMacAddress(void)
{
const uint8_t kExtAddr[OT_EXT_ADDRESS_SIZE] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
const Mac::ShortAddress kShortAddr = 0x1234;
Instance *instance;
Mac::Address addr;
Mac::ExtAddress extAddr;
uint8_t buffer[OT_EXT_ADDRESS_SIZE];
instance = testInitInstance();
VerifyOrQuit(instance != nullptr, "nullptr instance\n");
// Mac::ExtAddress
extAddr.GenerateRandom();
VerifyOrQuit(extAddr.IsLocal(), "Random Extended Address should have its Local bit set");
VerifyOrQuit(!extAddr.IsGroup(), "Random Extended Address should not have its Group bit set");
extAddr.CopyTo(buffer);
VerifyOrQuit(memcmp(extAddr.m8, buffer, OT_EXT_ADDRESS_SIZE) == 0);
extAddr.CopyTo(buffer, Mac::ExtAddress::kReverseByteOrder);
VerifyOrQuit(CompareReversed(extAddr.m8, buffer, OT_EXT_ADDRESS_SIZE));
extAddr.Set(kExtAddr);
VerifyOrQuit(memcmp(extAddr.m8, kExtAddr, OT_EXT_ADDRESS_SIZE) == 0);
extAddr.Set(kExtAddr, Mac::ExtAddress::kReverseByteOrder);
VerifyOrQuit(CompareReversed(extAddr.m8, kExtAddr, OT_EXT_ADDRESS_SIZE));
extAddr.SetLocal(true);
VerifyOrQuit(extAddr.IsLocal());
extAddr.SetLocal(false);
VerifyOrQuit(!extAddr.IsLocal());
extAddr.ToggleLocal();
VerifyOrQuit(extAddr.IsLocal());
extAddr.ToggleLocal();
VerifyOrQuit(!extAddr.IsLocal());
extAddr.SetGroup(true);
VerifyOrQuit(extAddr.IsGroup());
extAddr.SetGroup(false);
VerifyOrQuit(!extAddr.IsGroup());
extAddr.ToggleGroup();
VerifyOrQuit(extAddr.IsGroup());
extAddr.ToggleGroup();
VerifyOrQuit(!extAddr.IsGroup());
// Mac::Address
VerifyOrQuit(addr.IsNone(), "Address constructor failed");
VerifyOrQuit(addr.GetType() == Mac::Address::kTypeNone);
addr.SetShort(kShortAddr);
VerifyOrQuit(addr.GetType() == Mac::Address::kTypeShort);
VerifyOrQuit(addr.IsShort(), "Address::SetShort() failed");
VerifyOrQuit(!addr.IsExtended(), "Address::SetShort() failed");
VerifyOrQuit(addr.GetShort() == kShortAddr);
addr.SetExtended(extAddr);
VerifyOrQuit(addr.GetType() == Mac::Address::kTypeExtended);
VerifyOrQuit(!addr.IsShort(), "Address::SetExtended() failed");
VerifyOrQuit(addr.IsExtended(), "Address::SetExtended() failed");
VerifyOrQuit(addr.GetExtended() == extAddr);
addr.SetExtended(extAddr.m8, Mac::ExtAddress::kReverseByteOrder);
VerifyOrQuit(addr.GetType() == Mac::Address::kTypeExtended);
VerifyOrQuit(!addr.IsShort(), "Address::SetExtended() failed");
VerifyOrQuit(addr.IsExtended(), "Address::SetExtended() failed");
VerifyOrQuit(CompareReversed(addr.GetExtended().m8, extAddr.m8, OT_EXT_ADDRESS_SIZE),
"Address::SetExtended() reverse byte order failed");
addr.SetNone();
VerifyOrQuit(addr.GetType() == Mac::Address::kTypeNone);
VerifyOrQuit(addr.IsNone(), "Address:SetNone() failed");
VerifyOrQuit(!addr.IsShort(), "Address::SetNone() failed");
VerifyOrQuit(!addr.IsExtended(), "Address::SetNone() failed");
VerifyOrQuit(!addr.IsBroadcast(), "Address:SetNone() failed");
VerifyOrQuit(!addr.IsShortAddrInvalid());
addr.SetExtended(extAddr);
VerifyOrQuit(!addr.IsBroadcast());
VerifyOrQuit(!addr.IsShortAddrInvalid());
addr.SetShort(kShortAddr);
VerifyOrQuit(!addr.IsBroadcast());
VerifyOrQuit(!addr.IsShortAddrInvalid());
addr.SetShort(Mac::kShortAddrBroadcast);
VerifyOrQuit(addr.IsBroadcast());
VerifyOrQuit(!addr.IsShortAddrInvalid());
addr.SetShort(Mac::kShortAddrInvalid);
VerifyOrQuit(!addr.IsBroadcast());
VerifyOrQuit(addr.IsShortAddrInvalid());
testFreeInstance(instance);
}
void TestMacHeader(void)
{
enum AddrType : uint8_t
{
kNoneAddr,
kShrtAddr,
kExtdAddr,
};
enum PanIdMode
{
kNoPanId,
kUsePanId1,
kUsePanId2,
};
struct TestCase
{
Mac::Frame::Version mVersion;
AddrType mSrcAddrType;
PanIdMode mSrcPanIdMode;
AddrType mDstAddrType;
PanIdMode mDstPanIdMode;
Mac::Frame::SecurityLevel mSecurity;
Mac::Frame::KeyIdMode mKeyIdMode;
uint8_t mHeaderLength;
uint8_t mFooterLength;
bool mSuppressSequence;
};
static constexpr Mac::Frame::Version kVer2006 = Mac::Frame::kVersion2006;
static constexpr Mac::Frame::Version kVer2015 = Mac::Frame::kVersion2015;
static constexpr Mac::Frame::SecurityLevel kNoSec = Mac::Frame::kSecurityNone;
static constexpr Mac::Frame::SecurityLevel kMic32 = Mac::Frame::kSecurityMic32;
static constexpr Mac::Frame::KeyIdMode kModeId1 = Mac::Frame::kKeyIdMode1;
static constexpr Mac::Frame::KeyIdMode kModeId2 = Mac::Frame::kKeyIdMode2;
static const char *kAddrTypeStrings[] = {"None", "Short", "Extd"};
static const char *kPanIdModeStrings[] = {"No", "Id1", "Id2"};
static constexpr TestCase kTestCases[] = {
{kVer2006, kNoneAddr, kNoPanId, kNoneAddr, kNoPanId, kNoSec, kModeId1, 3, 2},
{kVer2006, kShrtAddr, kUsePanId1, kNoneAddr, kNoPanId, kNoSec, kModeId1, 7, 2},
{kVer2006, kExtdAddr, kUsePanId1, kNoneAddr, kNoPanId, kNoSec, kModeId1, 13, 2},
{kVer2006, kNoneAddr, kNoPanId, kShrtAddr, kUsePanId1, kNoSec, kModeId1, 7, 2},
{kVer2006, kNoneAddr, kNoPanId, kExtdAddr, kUsePanId1, kNoSec, kModeId1, 13, 2},
{kVer2006, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId2, kNoSec, kModeId1, 11, 2},
{kVer2006, kShrtAddr, kUsePanId1, kExtdAddr, kUsePanId2, kNoSec, kModeId1, 17, 2},
{kVer2006, kExtdAddr, kUsePanId1, kShrtAddr, kUsePanId2, kNoSec, kModeId1, 17, 2},
{kVer2006, kExtdAddr, kUsePanId1, kExtdAddr, kUsePanId2, kNoSec, kModeId1, 23, 2},
{kVer2006, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId1, kNoSec, kModeId1, 9, 2},
{kVer2006, kShrtAddr, kUsePanId1, kExtdAddr, kUsePanId1, kNoSec, kModeId1, 15, 2},
{kVer2006, kExtdAddr, kUsePanId1, kShrtAddr, kUsePanId1, kNoSec, kModeId1, 15, 2},
{kVer2006, kExtdAddr, kUsePanId1, kExtdAddr, kUsePanId1, kNoSec, kModeId1, 21, 2},
{kVer2006, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId1, kMic32, kModeId1, 15, 6},
{kVer2006, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId1, kMic32, kModeId2, 19, 6},
{kVer2015, kNoneAddr, kNoPanId, kNoneAddr, kNoPanId, kNoSec, kModeId1, 3, 2},
{kVer2015, kShrtAddr, kUsePanId1, kNoneAddr, kNoPanId, kNoSec, kModeId1, 7, 2},
{kVer2015, kExtdAddr, kUsePanId1, kNoneAddr, kNoPanId, kNoSec, kModeId1, 13, 2},
{kVer2015, kNoneAddr, kNoPanId, kShrtAddr, kUsePanId1, kNoSec, kModeId1, 7, 2},
{kVer2015, kNoneAddr, kNoPanId, kExtdAddr, kUsePanId1, kNoSec, kModeId1, 13, 2},
{kVer2015, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId2, kNoSec, kModeId1, 11, 2},
{kVer2015, kShrtAddr, kUsePanId1, kExtdAddr, kUsePanId2, kNoSec, kModeId1, 17, 2},
{kVer2015, kExtdAddr, kUsePanId1, kShrtAddr, kUsePanId2, kNoSec, kModeId1, 17, 2},
{kVer2015, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId1, kNoSec, kModeId1, 9, 2},
{kVer2015, kShrtAddr, kUsePanId1, kExtdAddr, kUsePanId1, kNoSec, kModeId1, 15, 2},
{kVer2015, kExtdAddr, kUsePanId1, kShrtAddr, kUsePanId1, kNoSec, kModeId1, 15, 2},
{kVer2015, kExtdAddr, kUsePanId1, kExtdAddr, kUsePanId1, kNoSec, kModeId1, 21, 2},
{kVer2015, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId1, kMic32, kModeId1, 15, 6},
{kVer2015, kShrtAddr, kUsePanId1, kShrtAddr, kUsePanId1, kMic32, kModeId2, 19, 6},
{kVer2015, kNoneAddr, kNoPanId, kShrtAddr, kNoPanId, kNoSec, kModeId1, 5, 2},
{kVer2015, kNoneAddr, kNoPanId, kShrtAddr, kNoPanId, kMic32, kModeId1, 11, 6},
{kVer2015, kNoneAddr, kNoPanId, kNoneAddr, kUsePanId1, kNoSec, kModeId1, 5, 2},
{kVer2015, kNoneAddr, kNoPanId, kNoneAddr, kUsePanId1, kMic32, kModeId1, 11, 6},
{kVer2015, kNoneAddr, kNoPanId, kShrtAddr, kNoPanId, kNoSec, kModeId1, 5, 2},
{kVer2015, kNoneAddr, kNoPanId, kExtdAddr, kNoPanId, kNoSec, kModeId1, 11, 2},
{kVer2015, kNoneAddr, kNoPanId, kShrtAddr, kNoPanId, kMic32, kModeId1, 11, 6},
{kVer2015, kNoneAddr, kNoPanId, kExtdAddr, kNoPanId, kMic32, kModeId1, 17, 6},
{kVer2015, kShrtAddr, kNoPanId, kNoneAddr, kNoPanId, kNoSec, kModeId1, 5, 2},
{kVer2015, kShrtAddr, kNoPanId, kNoneAddr, kNoPanId, kMic32, kModeId1, 11, 6},
{kVer2015, kExtdAddr, kNoPanId, kNoneAddr, kNoPanId, kNoSec, kModeId1, 11, 2},
{kVer2015, kExtdAddr, kNoPanId, kNoneAddr, kNoPanId, kMic32, kModeId1, 17, 6},
{kVer2015, kExtdAddr, kNoPanId, kExtdAddr, kNoPanId, kNoSec, kModeId1, 19, 2},
{kVer2015, kExtdAddr, kNoPanId, kExtdAddr, kNoPanId, kMic32, kModeId1, 25, 6},
{kVer2015, kExtdAddr, kNoPanId, kExtdAddr, kNoPanId, kMic32, kModeId1, 24, 6, true},
};
const uint16_t kPanId1 = 0xbaba;
const uint16_t kPanId2 = 0xdede;
const uint16_t kShortAddr1 = 0x1234;
const uint16_t kShortAddr2 = 0x5678;
const uint8_t kExtAddr1[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
const uint8_t kExtAddr2[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
char string[100];
Mac::ExtAddress extAddr1;
Mac::ExtAddress extAddr2;
extAddr1.Set(kExtAddr1);
extAddr2.Set(kExtAddr2);
printf("TestMacHeader\n");
for (const TestCase &testCase : kTestCases)
{
uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE];
uint8_t offset;
Mac::TxFrame frame;
Mac::TxFrame::Info frameInfo;
Mac::Address address;
Mac::PanId panId;
frame.mPsdu = psdu;
frame.mLength = 0;
frame.mRadioType = 0;
VerifyOrQuit(frameInfo.mAddrs.mSource.IsNone());
VerifyOrQuit(frameInfo.mAddrs.mDestination.IsNone());
VerifyOrQuit(!frameInfo.mPanIds.IsSourcePresent());
VerifyOrQuit(!frameInfo.mPanIds.IsDestinationPresent());
switch (testCase.mSrcAddrType)
{
case kNoneAddr:
frameInfo.mAddrs.mSource.SetNone();
break;
case kShrtAddr:
frameInfo.mAddrs.mSource.SetShort(kShortAddr1);
break;
case kExtdAddr:
frameInfo.mAddrs.mSource.SetExtended(extAddr1);
break;
}
switch (testCase.mDstAddrType)
{
case kNoneAddr:
frameInfo.mAddrs.mDestination.SetNone();
break;
case kShrtAddr:
frameInfo.mAddrs.mDestination.SetShort(kShortAddr2);
break;
case kExtdAddr:
frameInfo.mAddrs.mDestination.SetExtended(extAddr2);
break;
}
switch (testCase.mSrcPanIdMode)
{
case kNoPanId:
break;
case kUsePanId1:
frameInfo.mPanIds.SetSource(kPanId1);
break;
case kUsePanId2:
frameInfo.mPanIds.SetSource(kPanId2);
break;
}
switch (testCase.mDstPanIdMode)
{
case kNoPanId:
break;
case kUsePanId1:
frameInfo.mPanIds.SetDestination(kPanId1);
break;
case kUsePanId2:
frameInfo.mPanIds.SetDestination(kPanId2);
break;
}
frameInfo.mType = Mac::Frame::kTypeData;
frameInfo.mVersion = testCase.mVersion;
frameInfo.mSecurityLevel = testCase.mSecurity;
frameInfo.mKeyIdMode = testCase.mKeyIdMode;
frameInfo.mSuppressSequence = testCase.mSuppressSequence;
frameInfo.PrepareHeadersIn(frame);
VerifyOrQuit(frame.GetHeaderLength() == testCase.mHeaderLength);
VerifyOrQuit(frame.GetFooterLength() == testCase.mFooterLength);
VerifyOrQuit(frame.GetLength() == testCase.mHeaderLength + testCase.mFooterLength);
VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeData);
VerifyOrQuit(!frame.IsAck());
VerifyOrQuit(frame.GetVersion() == testCase.mVersion);
VerifyOrQuit(frame.GetSecurityEnabled() == (testCase.mSecurity != kNoSec));
VerifyOrQuit(!frame.GetFramePending());
VerifyOrQuit(!frame.IsIePresent());
VerifyOrQuit(frame.GetAckRequest() == (testCase.mDstAddrType != kNoneAddr));
VerifyOrQuit(frame.IsSrcAddrPresent() == (testCase.mSrcAddrType != kNoneAddr));
SuccessOrQuit(frame.GetSrcAddr(address));
VerifyOrQuit(CompareAddresses(address, frameInfo.mAddrs.mSource));
VerifyOrQuit(frame.IsDstAddrPresent() == (testCase.mDstAddrType != kNoneAddr));
SuccessOrQuit(frame.GetDstAddr(address));
VerifyOrQuit(CompareAddresses(address, frameInfo.mAddrs.mDestination));
VerifyOrQuit(frame.IsDstPanIdPresent() == (testCase.mDstPanIdMode != kNoPanId));
if (frame.IsDstPanIdPresent())
{
SuccessOrQuit(frame.GetDstPanId(panId));
VerifyOrQuit(panId == frameInfo.mPanIds.GetDestination());
VerifyOrQuit(frameInfo.mPanIds.IsDestinationPresent());
}
if (frame.IsSrcPanIdPresent())
{
SuccessOrQuit(frame.GetSrcPanId(panId));
VerifyOrQuit(panId == frameInfo.mPanIds.GetSource());
VerifyOrQuit(frameInfo.mPanIds.IsSourcePresent());
}
if (frame.GetSecurityEnabled())
{
uint8_t security;
uint8_t keyIdMode;
SuccessOrQuit(frame.GetSecurityLevel(security));
VerifyOrQuit(security == testCase.mSecurity);
SuccessOrQuit(frame.GetKeyIdMode(keyIdMode));
VerifyOrQuit(keyIdMode == testCase.mKeyIdMode);
}
offset = snprintf(string, sizeof(string), "\nver:%s, src[addr:%s, pan:%s], dst[addr:%s, pan:%s], sec:%s",
(testCase.mVersion == kVer2006) ? "2006" : "2015", kAddrTypeStrings[testCase.mSrcAddrType],
kPanIdModeStrings[testCase.mSrcPanIdMode], kAddrTypeStrings[testCase.mDstAddrType],
kPanIdModeStrings[testCase.mDstPanIdMode], testCase.mSecurity == kNoSec ? "no" : "mic32");
if (!testCase.mSuppressSequence)
{
VerifyOrQuit(frame.IsSequencePresent());
offset += snprintf(string + offset, sizeof(string) - offset, ", seq:%u", frame.GetSequence());
}
else
{
VerifyOrQuit(!frame.IsSequencePresent());
}
DumpBuffer(string, frame.GetPsdu(), frame.GetLength());
}
}
void VerifyChannelMaskContent(const Mac::ChannelMask &aMask, uint8_t *aChannels, uint8_t aLength)
{
uint8_t index = 0;
uint8_t channel;
for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax; channel++)
{
if (index < aLength)
{
if (channel == aChannels[index])
{
index++;
VerifyOrQuit(aMask.ContainsChannel(channel));
}
else
{
VerifyOrQuit(!aMask.ContainsChannel(channel));
}
}
}
index = 0;
channel = Mac::ChannelMask::kChannelIteratorFirst;
while (aMask.GetNextChannel(channel) == kErrorNone)
{
VerifyOrQuit(channel == aChannels[index++], "ChannelMask.GetNextChannel() failed");
}
VerifyOrQuit(index == aLength, "ChannelMask.GetNextChannel() failed");
if (aLength == 1)
{
VerifyOrQuit(aMask.IsSingleChannel());
}
else
{
VerifyOrQuit(!aMask.IsSingleChannel());
}
VerifyOrQuit(aLength == aMask.GetNumberOfChannels());
}
void TestMacChannelMask(void)
{
uint8_t allChannels[] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26};
uint8_t channels1[] = {11, 14, 15, 16, 17, 20, 21, 22, 24, 25};
uint8_t channels2[] = {14, 21, 26};
uint8_t channels3[] = {14, 21};
uint8_t channles4[] = {20};
static const char kEmptyMaskString[] = "{ }";
static const char kAllChannelsString[] = "{ 11-26 }";
static const char kChannels1String[] = "{ 11, 14-17, 20-22, 24, 25 }";
static const char kChannels2String[] = "{ 14, 21, 26 }";
static const char kChannels3String[] = "{ 14, 21 }";
static const char kChannels4String[] = "{ 20 }";
Mac::ChannelMask mask1;
Mac::ChannelMask mask2(Radio::kSupportedChannels);
printf("Testing Mac::ChannelMask\n");
VerifyOrQuit(mask1.IsEmpty());
printf("empty = %s\n", mask1.ToString().AsCString());
VerifyOrQuit(strcmp(mask1.ToString().AsCString(), kEmptyMaskString) == 0);
VerifyOrQuit(!mask2.IsEmpty());
VerifyOrQuit(mask2.GetMask() == Radio::kSupportedChannels);
printf("allChannels = %s\n", mask2.ToString().AsCString());
VerifyOrQuit(strcmp(mask2.ToString().AsCString(), kAllChannelsString) == 0);
mask1.SetMask(Radio::kSupportedChannels);
VerifyOrQuit(!mask1.IsEmpty());
VerifyOrQuit(mask1.GetMask() == Radio::kSupportedChannels);
VerifyChannelMaskContent(mask1, allChannels, sizeof(allChannels));
// Test ChannelMask::RemoveChannel()
for (size_t index = 0; index < sizeof(allChannels) - 1; index++)
{
mask1.RemoveChannel(allChannels[index]);
VerifyChannelMaskContent(mask1, &allChannels[index + 1], sizeof(allChannels) - 1 - index);
}
mask1.Clear();
VerifyOrQuit(mask1.IsEmpty());
VerifyChannelMaskContent(mask1, nullptr, 0);
for (uint8_t channel : channels1)
{
mask1.AddChannel(channel);
}
printf("channels1 = %s\n", mask1.ToString().AsCString());
VerifyOrQuit(strcmp(mask1.ToString().AsCString(), kChannels1String) == 0);
VerifyOrQuit(!mask1.IsEmpty());
VerifyChannelMaskContent(mask1, channels1, sizeof(channels1));
mask2.Clear();
for (uint8_t channel : channels2)
{
mask2.AddChannel(channel);
}
printf("channels2 = %s\n", mask2.ToString().AsCString());
VerifyOrQuit(strcmp(mask2.ToString().AsCString(), kChannels2String) == 0);
VerifyOrQuit(!mask2.IsEmpty());
VerifyChannelMaskContent(mask2, channels2, sizeof(channels2));
mask1.Intersect(mask2);
VerifyChannelMaskContent(mask1, channels3, sizeof(channels3));
printf("channels3 = %s\n", mask1.ToString().AsCString());
VerifyOrQuit(strcmp(mask1.ToString().AsCString(), kChannels3String) == 0);
mask2.Clear();
mask2.AddChannel(channles4[0]);
VerifyChannelMaskContent(mask2, channles4, sizeof(channles4));
printf("channels4 = %s\n", mask2.ToString().AsCString());
VerifyOrQuit(strcmp(mask2.ToString().AsCString(), kChannels4String) == 0);
mask1.Clear();
mask2.Clear();
VerifyOrQuit(mask1 == mask2);
mask1.SetMask(Radio::kSupportedChannels);
mask2.SetMask(Radio::kSupportedChannels);
VerifyOrQuit(mask1 == mask2);
mask1.Clear();
VerifyOrQuit(mask1 != mask2);
}
void TestMacFrameApi(void)
{
uint8_t ack_psdu1[] = {0x02, 0x10, 0x5e, 0xd2, 0x9b};
uint8_t mac_cmd_psdu1[] = {0x6b, 0xdc, 0x85, 0xce, 0xfa, 0x47, 0x36, 0x07, 0xd9, 0x74, 0x45, 0x8d,
0xb2, 0x6e, 0x81, 0x25, 0xc9, 0xdb, 0xac, 0x2b, 0x0a, 0x0d, 0x00, 0x00,
0x00, 0x00, 0x01, 0x04, 0xaf, 0x14, 0xce, 0xaa, 0x5a, 0xe5};
Mac::Frame frame;
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
uint8_t data_psdu1[] = {0x29, 0xee, 0x53, 0xce, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x05,
0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x01};
uint8_t mac_cmd_psdu2[] = {0x6b, 0xaa, 0x8d, 0xce, 0xfa, 0x00, 0x68, 0x01, 0x68, 0x0d,
0x08, 0x00, 0x00, 0x00, 0x01, 0x04, 0x0d, 0xed, 0x0b, 0x35,
0x0c, 0x80, 0x3f, 0x04, 0x4b, 0x88, 0x89, 0xd6, 0x59, 0xe1};
uint8_t scf; // SecurityControlField
#endif
// Imm-Ack, Sequence Number: 94
// Frame Control Field: 0x1002
// .... .... .... .010 = Frame Type: Ack (0x2)
// .... .... .... 0... = Security Enabled: False
// .... .... ...0 .... = Frame Pending: False
// .... .... ..0. .... = Acknowledge Request: False
// .... .... .0.. .... = PAN ID Compression: False
// .... ...0 .... .... = Sequence Number Suppression: False
// .... ..0. .... .... = Information Elements Present: False
// .... 00.. .... .... = Destination Addressing Mode: None (0x0)
// ..01 .... .... .... = Frame Version: IEEE Std 802.15.4-2006 (1)
// 00.. .... .... .... = Source Addressing Mode: None (0x0)
// Sequence Number: 94
// FCS: 0x9bd2 (Correct)
frame.mPsdu = ack_psdu1;
frame.mLength = sizeof(ack_psdu1);
VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeAck);
VerifyOrQuit(!frame.GetSecurityEnabled());
VerifyOrQuit(!frame.GetFramePending());
VerifyOrQuit(!frame.GetAckRequest());
VerifyOrQuit(!frame.IsIePresent());
VerifyOrQuit(!frame.IsDstPanIdPresent());
VerifyOrQuit(!frame.IsDstAddrPresent());
VerifyOrQuit(frame.GetVersion() == Mac::Frame::kVersion2006);
VerifyOrQuit(!frame.IsSrcAddrPresent());
VerifyOrQuit(frame.IsSequencePresent());
VerifyOrQuit(frame.GetSequence() == 94);
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
// IEEE 802.15.4-2015 Data
// Sequence Number: 83
// Destination PAN: 0xface
// Destination: 16:6e:0a:00:00:00:00:01
// Extended Source: 16:6e:0a:00:00:00:00:05
// Auxiliary Security Header
// Security Control Field: 0x0d
frame.mPsdu = data_psdu1;
frame.mLength = sizeof(data_psdu1);
VerifyOrQuit(frame.IsVersion2015());
VerifyOrQuit(frame.IsDstPanIdPresent());
VerifyOrQuit(frame.IsDstAddrPresent());
VerifyOrQuit(frame.IsSrcAddrPresent());
SuccessOrQuit(frame.GetSecurityControlField(scf));
VerifyOrQuit(scf == 0x0d);
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
// IEEE 802.15.4-2006 Mac Command
// Sequence Number: 133
// Command Identifier: Data Request (0x04)
uint8_t commandId;
frame.mPsdu = mac_cmd_psdu1;
frame.mLength = sizeof(mac_cmd_psdu1);
VerifyOrQuit(frame.IsSequencePresent());
VerifyOrQuit(frame.GetSequence() == 133);
VerifyOrQuit(frame.GetVersion() == Mac::Frame::kVersion2006);
VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeMacCmd);
SuccessOrQuit(frame.GetCommandId(commandId));
VerifyOrQuit(commandId == Mac::Frame::kMacCmdDataRequest);
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
// IEEE 802.15.4-2015 Mac Command
// Sequence Number: 141
// Header IEs
// CSL IE
// Header Termination 2 IE (Payload follows)
// Command Identifier: Data Request (0x04)
frame.mPsdu = mac_cmd_psdu2;
frame.mLength = sizeof(mac_cmd_psdu2);
VerifyOrQuit(frame.IsSequencePresent());
VerifyOrQuit(frame.GetSequence() == 141);
VerifyOrQuit(frame.IsVersion2015());
VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeMacCmd);
SuccessOrQuit(frame.GetCommandId(commandId));
VerifyOrQuit(commandId == Mac::Frame::kMacCmdDataRequest);
printf("commandId:%d\n", commandId);
#endif
}
void TestMacFrameAckGeneration(void)
{
constexpr uint8_t kImmAckLength = 5;
Mac::RxFrame receivedFrame;
Mac::TxFrame ackFrame;
uint8_t ackFrameBuffer[100];
ackFrame.mPsdu = ackFrameBuffer;
ackFrame.mLength = sizeof(ackFrameBuffer);
// Received Frame 1
// IEEE 802.15.4 Data
// Frame Control Field: 0xdc61
// .... .... .... .001 = Frame Type: Data (0x1)
// .... .... .... 0... = Security Enabled: False
// .... .... ...0 .... = Frame Pending: False
// .... .... ..1. .... = Acknowledge Request: True
// .... .... .1.. .... = PAN ID Compression: True
// .... ...0 .... .... = Sequence Number Suppression: False
// .... ..0. .... .... = Information Elements Present: False
// .... 11.. .... .... = Destination Addressing Mode: Long/64-bit (0x3)
// ..01 .... .... .... = Frame Version: IEEE Std 802.15.4-2006 (1)
// 11.. .... .... .... = Source Addressing Mode: Long/64-bit (0x3)
// Sequence Number: 189
// Destination PAN: 0xface
// Destination: 16:6e:0a:00:00:00:00:01 (16:6e:0a:00:00:00:00:01)
// Extended Source: 16:6e:0a:00:00:00:00:02 (16:6e:0a:00:00:00:00:02)
uint8_t data_psdu1[] = {0x61, 0xdc, 0xbd, 0xce, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x02,
0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x7f, 0x33, 0xf0, 0x4d, 0x4c, 0x4d, 0x4c,
0x8b, 0xf0, 0x00, 0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc2,
0x57, 0x9c, 0x31, 0xb3, 0x2a, 0xa1, 0x86, 0xba, 0x9a, 0xed, 0x5a, 0xb9, 0xa3, 0x59,
0x88, 0xeb, 0xbb, 0x0d, 0xc3, 0xed, 0xeb, 0x8a, 0x53, 0xa6, 0xed, 0xf7, 0xdd, 0x45,
0x6e, 0xf7, 0x9a, 0x17, 0xb4, 0xab, 0xc6, 0x75, 0x71, 0x46, 0x37, 0x93, 0x4a, 0x32,
0xb1, 0x21, 0x9f, 0x9d, 0xb3, 0x65, 0x27, 0xd5, 0xfc, 0x50, 0x16, 0x90, 0xd2, 0xd4};
receivedFrame.mPsdu = data_psdu1;
receivedFrame.mLength = sizeof(data_psdu1);
ackFrame.GenerateImmAck(receivedFrame, false);
VerifyOrQuit(ackFrame.mLength == kImmAckLength);
VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kTypeAck);
VerifyOrQuit(!ackFrame.GetSecurityEnabled());
VerifyOrQuit(!ackFrame.GetFramePending());
VerifyOrQuit(!ackFrame.GetAckRequest());
VerifyOrQuit(!ackFrame.IsIePresent());
VerifyOrQuit(!ackFrame.IsDstPanIdPresent());
VerifyOrQuit(!ackFrame.IsDstAddrPresent());
VerifyOrQuit(!ackFrame.IsSrcAddrPresent());
VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2006);
VerifyOrQuit(ackFrame.IsSequencePresent());
VerifyOrQuit(ackFrame.GetSequence() == 189);
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
// Received Frame 2
// IEEE 802.15.4 Data
// Frame Control Field: 0xa869, Frame Type: Data, Security Enabled, Acknowledge Request, PAN ID Compression,
// Destination Addressing Mode: Short/16-bit, Frame Version: IEEE Std 802.15.4-2015, Source Addressing Mode:
// Short/16-bit
// .... .... .... .001 = Frame Type: Data (0x1)
// .... .... .... 1... = Security Enabled: True
// .... .... ...0 .... = Frame Pending: False
// .... .... ..1. .... = Acknowledge Request: True
// .... .... .1.. .... = PAN ID Compression: True
// .... ...0 .... .... = Sequence Number Suppression: False
// .... ..0. .... .... = Information Elements Present: False
// .... 10.. .... .... = Destination Addressing Mode: Short/16-bit (0x2)
// ..10 .... .... .... = Frame Version: IEEE Std 802.15.4-2015 (2)
// 10.. .... .... .... = Source Addressing Mode: Short/16-bit (0x2)
// Sequence Number: 142
// Destination PAN: 0xface
// Destination: 0x2402
// Source: 0x2400
// [Extended Source: 16:6e:0a:00:00:00:00:01 (16:6e:0a:00:00:00:00:01)]
// [Origin: 2]
// Auxiliary Security Header
// Security Control Field: 0x0d, Security Level: Encryption with 32-bit Message Integrity Code, Key Identifier
// Mode: Indexed Key using the Default Key Source
// .... .101 = Security Level: Encryption with 32-bit Message Integrity Code (0x5)
// ...0 1... = Key Identifier Mode: Indexed Key using the Default Key Source (0x1)
// ..0. .... = Frame Counter Suppression: False
// .0.. .... = ASN in Nonce: False
// 0... .... = Reserved: 0x0
// Frame Counter: 2
// Key Identifier Field
// Key Index: 0x01
// MIC: f94e5870
// [Key Number: 0]
// FCS: 0x8c40 (Correct)
uint8_t data_psdu2[] = {0x69, 0xa8, 0x8e, 0xce, 0xfa, 0x02, 0x24, 0x00, 0x24, 0x0d, 0x02,
0x00, 0x00, 0x00, 0x01, 0x6b, 0x64, 0x60, 0x08, 0x55, 0xb8, 0x10,
0x18, 0xc7, 0x40, 0x2e, 0xfb, 0xf3, 0xda, 0xf9, 0x4e, 0x58, 0x70};
receivedFrame.mPsdu = data_psdu2;
receivedFrame.mLength = sizeof(data_psdu2);
uint8_t ie_data[6] = {0x04, 0x0d, 0x21, 0x0c, 0x35, 0x0c};
Mac::CslIe *csl;
SuccessOrQuit(ackFrame.GenerateEnhAck(receivedFrame, false, ie_data, sizeof(ie_data)));
csl = ackFrame.Find<Mac::CslIe>();
VerifyOrQuit(ackFrame.mLength == 25);
VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kTypeAck);
VerifyOrQuit(ackFrame.GetSecurityEnabled());
VerifyOrQuit(ackFrame.IsIePresent());
VerifyOrQuit(ackFrame.IsDstPanIdPresent());
VerifyOrQuit(ackFrame.IsDstAddrPresent());
VerifyOrQuit(!ackFrame.IsSrcAddrPresent());
VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2015);
VerifyOrQuit(ackFrame.IsSequencePresent());
VerifyOrQuit(ackFrame.GetSequence() == 142);
VerifyOrQuit(csl->GetPeriod() == 3125 && csl->GetPhase() == 3105);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
ackFrame.UpdateCslIe(123, 456);
csl = ackFrame.Find<Mac::CslIe>();
VerifyOrQuit(csl->GetPeriod() == 123 && csl->GetPhase() == 456);
#endif
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
}
} // namespace ot
int main(void)
{
ot::TestMacAddress();
ot::TestMacHeader();
ot::TestMacChannelMask();
ot::TestMacFrameApi();
ot::TestMacFrameAckGeneration();
printf("All tests passed\n");
return 0;
}