mirror of
https://github.com/espressif/openthread.git
synced 2026-08-24 19:29:52 +00:00
[mac] support frames without sequence number (#10544)
This commit adds the capability to support frames without sequence number.
This commit is contained in:
@@ -606,7 +606,11 @@ static void radioReceive(otInstance *aInstance)
|
||||
{
|
||||
if (otMacFrameIsAckRequested(&sTransmitFrame))
|
||||
{
|
||||
isTxDone = isAck && otMacFrameGetSequence(&sReceiveFrame) == otMacFrameGetSequence(&sTransmitFrame);
|
||||
uint8_t rxSeq;
|
||||
uint8_t txSeq;
|
||||
|
||||
isTxDone = isAck && otMacFrameGetSequence(&sReceiveFrame, &rxSeq) == OT_ERROR_NONE &&
|
||||
otMacFrameGetSequence(&sTransmitFrame, &txSeq) == OT_ERROR_NONE && rxSeq == txSeq;
|
||||
}
|
||||
#if OPENTHREAD_SIMULATION_VIRTUAL_TIME
|
||||
// Simulate tx done when receiving the echo frame.
|
||||
|
||||
@@ -139,9 +139,21 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t otMacFrameGetSequence(const otRadioFrame *aFrame)
|
||||
otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence)
|
||||
{
|
||||
return static_cast<const Mac::Frame *>(aFrame)->GetSequence();
|
||||
otError error;
|
||||
|
||||
if (static_cast<const Mac::Frame *>(aFrame)->IsSequencePresent())
|
||||
{
|
||||
*aSequence = static_cast<const Mac::Frame *>(aFrame)->GetSequence();
|
||||
error = kErrorNone;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = kErrorParse;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void otMacFrameProcessTransmitAesCcm(otRadioFrame *aFrame, const otExtAddress *aExtAddress)
|
||||
|
||||
@@ -169,11 +169,13 @@ otError otMacFrameGetDstAddr(const otRadioFrame *aFrame, otMacAddress *aMacAddre
|
||||
* Get the sequence of @p aFrame.
|
||||
*
|
||||
* @param[in] aFrame A pointer to the frame.
|
||||
* @param[out] aSequence A pointer to the sequence.
|
||||
*
|
||||
* @returns The sequence of the frame.
|
||||
* @retval OT_ERROR_NONE Successfully got the sequence.
|
||||
* @retval OT_ERROR_PARSE Failed to parse the sequence.
|
||||
*
|
||||
*/
|
||||
uint8_t otMacFrameGetSequence(const otRadioFrame *aFrame);
|
||||
otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence);
|
||||
|
||||
/**
|
||||
* Performs AES CCM on the frame which is going to be sent.
|
||||
|
||||
@@ -59,7 +59,8 @@ void Frame::InitMacHeader(Type aType,
|
||||
const Addresses &aAddrs,
|
||||
const PanIds &aPanIds,
|
||||
SecurityLevel aSecurityLevel,
|
||||
KeyIdMode aKeyIdMode)
|
||||
KeyIdMode aKeyIdMode,
|
||||
bool aSuppressSequence)
|
||||
{
|
||||
uint16_t fcf;
|
||||
FrameBuilder builder;
|
||||
@@ -124,6 +125,9 @@ void Frame::InitMacHeader(Type aType,
|
||||
{
|
||||
fcf |= kFcfPanidCompression;
|
||||
}
|
||||
|
||||
// Sequence Number Suppression bit was reserved, and must not be set on initialization.
|
||||
OT_ASSERT(!aSuppressSequence);
|
||||
break;
|
||||
|
||||
case kVersion2015:
|
||||
@@ -194,9 +198,18 @@ void Frame::InitMacHeader(Type aType,
|
||||
break;
|
||||
}
|
||||
|
||||
if (aSuppressSequence)
|
||||
{
|
||||
fcf |= kFcfSequenceSupression;
|
||||
}
|
||||
|
||||
builder.Init(mPsdu, GetMtu());
|
||||
IgnoreError(builder.AppendLittleEndianUint16(fcf));
|
||||
IgnoreError(builder.AppendUint8(0)); // Seq number
|
||||
|
||||
if (!IsSequenceSuppressed(fcf))
|
||||
{
|
||||
IgnoreError(builder.AppendUint8(0)); // Seq number
|
||||
}
|
||||
|
||||
if (IsDstPanIdPresent(fcf))
|
||||
{
|
||||
@@ -294,7 +307,7 @@ uint8_t Frame::FindDstPanIdIndex(void) const
|
||||
|
||||
VerifyOrExit(IsDstPanIdPresent(), index = kInvalidIndex);
|
||||
|
||||
index = kFcfSize + kDsnSize;
|
||||
index = kFcfSize + GetSeqNumSize();
|
||||
|
||||
exit:
|
||||
return index;
|
||||
@@ -373,7 +386,22 @@ void Frame::SetDstPanId(PanId aPanId)
|
||||
LittleEndian::WriteUint16(aPanId, &mPsdu[index]);
|
||||
}
|
||||
|
||||
uint8_t Frame::FindDstAddrIndex(void) const { return kFcfSize + kDsnSize + (IsDstPanIdPresent() ? sizeof(PanId) : 0); }
|
||||
uint8_t Frame::GetSequence(void) const
|
||||
{
|
||||
OT_ASSERT(IsSequencePresent());
|
||||
return GetPsdu()[kSequenceIndex];
|
||||
}
|
||||
|
||||
void Frame::SetSequence(uint8_t aSequence)
|
||||
{
|
||||
OT_ASSERT(IsSequencePresent());
|
||||
GetPsdu()[kSequenceIndex] = aSequence;
|
||||
}
|
||||
|
||||
uint8_t Frame::FindDstAddrIndex(void) const
|
||||
{
|
||||
return kFcfSize + GetSeqNumSize() + (IsDstPanIdPresent() ? sizeof(PanId) : 0);
|
||||
}
|
||||
|
||||
Error Frame::GetDstAddr(Address &aAddress) const
|
||||
{
|
||||
@@ -442,7 +470,7 @@ uint8_t Frame::FindSrcPanIdIndex(void) const
|
||||
|
||||
VerifyOrExit(IsSrcPanIdPresent(), index = kInvalidIndex);
|
||||
|
||||
index += kFcfSize + kDsnSize;
|
||||
index += kFcfSize + GetSeqNumSize();
|
||||
|
||||
if (IsDstPanIdPresent(fcf))
|
||||
{
|
||||
@@ -533,7 +561,7 @@ uint8_t Frame::FindSrcAddrIndex(void) const
|
||||
uint8_t index = 0;
|
||||
uint16_t fcf = GetFrameControlField();
|
||||
|
||||
index += kFcfSize + kDsnSize;
|
||||
index += kFcfSize + GetSeqNumSize();
|
||||
|
||||
if (IsDstPanIdPresent(fcf))
|
||||
{
|
||||
@@ -941,7 +969,9 @@ uint8_t Frame::SkipAddrFieldIndex(void) const
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
VerifyOrExit(kFcfSize + kDsnSize + GetFcsSize() <= mLength, index = kInvalidIndex);
|
||||
VerifyOrExit(kFcfSize + GetFcsSize() <= mLength, index = kInvalidIndex);
|
||||
|
||||
VerifyOrExit(!IsSequencePresent() || kFcfSize + kDsnSize + GetFcsSize() <= mLength, index = kInvalidIndex);
|
||||
|
||||
index = CalculateAddrFieldSize(GetFrameControlField());
|
||||
|
||||
@@ -951,7 +981,7 @@ exit:
|
||||
|
||||
uint8_t Frame::CalculateAddrFieldSize(uint16_t aFcf)
|
||||
{
|
||||
uint8_t size = kFcfSize + kDsnSize;
|
||||
uint8_t size = kFcfSize + GetSeqNumSize(aFcf);
|
||||
|
||||
// This static method calculates the size (number of bytes) of
|
||||
// Address header field for a given Frame Control `aFcf` value.
|
||||
@@ -1538,7 +1568,14 @@ Frame::InfoString Frame::ToInfoString(void) const
|
||||
uint8_t commandId, type;
|
||||
Address src, dst;
|
||||
|
||||
string.Append("len:%d, seqnum:%d, type:", mLength, GetSequence());
|
||||
if (IsSequencePresent())
|
||||
{
|
||||
string.Append("len:%d, seqnum:%d, type:", mLength, GetSequence());
|
||||
}
|
||||
else
|
||||
{
|
||||
string.Append("len:%d, type:", mLength);
|
||||
}
|
||||
|
||||
type = GetType();
|
||||
|
||||
|
||||
+44
-18
@@ -386,6 +386,7 @@ public:
|
||||
* @param[in] aPanIds Source and destination PAN IDs.
|
||||
* @param[in] aSecurityLevel Frame security level.
|
||||
* @param[in] aKeyIdMode Frame security key ID mode.
|
||||
* @param[in] aSuppressSequence Whether to suppress sequence number.
|
||||
*
|
||||
*/
|
||||
void InitMacHeader(Type aType,
|
||||
@@ -393,7 +394,8 @@ public:
|
||||
const Addresses &aAddrs,
|
||||
const PanIds &aPanIds,
|
||||
SecurityLevel aSecurityLevel,
|
||||
KeyIdMode aKeyIdMode = kKeyIdMode0);
|
||||
KeyIdMode aKeyIdMode = kKeyIdMode0,
|
||||
bool aSuppressSequence = false);
|
||||
|
||||
/**
|
||||
* Validates the frame.
|
||||
@@ -512,7 +514,7 @@ public:
|
||||
* @returns The Sequence Number value.
|
||||
*
|
||||
*/
|
||||
uint8_t GetSequence(void) const { return GetPsdu()[kSequenceIndex]; }
|
||||
uint8_t GetSequence(void) const;
|
||||
|
||||
/**
|
||||
* Sets the Sequence Number value.
|
||||
@@ -520,7 +522,24 @@ public:
|
||||
* @param[in] aSequence The Sequence Number value.
|
||||
*
|
||||
*/
|
||||
void SetSequence(uint8_t aSequence) { GetPsdu()[kSequenceIndex] = aSequence; }
|
||||
void SetSequence(uint8_t aSequence);
|
||||
|
||||
/**
|
||||
* Indicates whether or not the Sequence Number is present.
|
||||
*
|
||||
* @returns TRUE if the Sequence Number is present, FALSE otherwise.
|
||||
*
|
||||
*/
|
||||
uint8_t IsSequencePresent(void) const { return !IsSequenceSuppressed(GetFrameControlField()); }
|
||||
|
||||
/**
|
||||
* Get the size of the sequence number.
|
||||
*
|
||||
* @retval 0 The size of sequence number is 0, indicating it's not present.
|
||||
* @retval 1 The size of sequence number is 1, indicating it's present.
|
||||
*
|
||||
*/
|
||||
uint8_t GetSeqNumSize(void) const { return GetSeqNumSize(GetFrameControlField()); }
|
||||
|
||||
/**
|
||||
* Indicates whether or not the Destination PAN ID is present.
|
||||
@@ -1089,21 +1108,22 @@ protected:
|
||||
static constexpr uint8_t kCommandIdSize = sizeof(uint8_t);
|
||||
static constexpr uint8_t kKeyIndexSize = sizeof(uint8_t);
|
||||
|
||||
static constexpr uint16_t kFcfFrameTypeMask = 7 << 0;
|
||||
static constexpr uint16_t kFcfSecurityEnabled = 1 << 3;
|
||||
static constexpr uint16_t kFcfFramePending = 1 << 4;
|
||||
static constexpr uint16_t kFcfAckRequest = 1 << 5;
|
||||
static constexpr uint16_t kFcfPanidCompression = 1 << 6;
|
||||
static constexpr uint16_t kFcfIePresent = 1 << 9;
|
||||
static constexpr uint16_t kFcfDstAddrNone = 0 << 10;
|
||||
static constexpr uint16_t kFcfDstAddrShort = 2 << 10;
|
||||
static constexpr uint16_t kFcfDstAddrExt = 3 << 10;
|
||||
static constexpr uint16_t kFcfDstAddrMask = 3 << 10;
|
||||
static constexpr uint16_t kFcfFrameVersionMask = 3 << 12;
|
||||
static constexpr uint16_t kFcfSrcAddrNone = 0 << 14;
|
||||
static constexpr uint16_t kFcfSrcAddrShort = 2 << 14;
|
||||
static constexpr uint16_t kFcfSrcAddrExt = 3 << 14;
|
||||
static constexpr uint16_t kFcfSrcAddrMask = 3 << 14;
|
||||
static constexpr uint16_t kFcfFrameTypeMask = 7 << 0;
|
||||
static constexpr uint16_t kFcfSecurityEnabled = 1 << 3;
|
||||
static constexpr uint16_t kFcfFramePending = 1 << 4;
|
||||
static constexpr uint16_t kFcfAckRequest = 1 << 5;
|
||||
static constexpr uint16_t kFcfPanidCompression = 1 << 6;
|
||||
static constexpr uint16_t kFcfSequenceSupression = 1 << 8;
|
||||
static constexpr uint16_t kFcfIePresent = 1 << 9;
|
||||
static constexpr uint16_t kFcfDstAddrNone = 0 << 10;
|
||||
static constexpr uint16_t kFcfDstAddrShort = 2 << 10;
|
||||
static constexpr uint16_t kFcfDstAddrExt = 3 << 10;
|
||||
static constexpr uint16_t kFcfDstAddrMask = 3 << 10;
|
||||
static constexpr uint16_t kFcfFrameVersionMask = 3 << 12;
|
||||
static constexpr uint16_t kFcfSrcAddrNone = 0 << 14;
|
||||
static constexpr uint16_t kFcfSrcAddrShort = 2 << 14;
|
||||
static constexpr uint16_t kFcfSrcAddrExt = 3 << 14;
|
||||
static constexpr uint16_t kFcfSrcAddrMask = 3 << 14;
|
||||
|
||||
static constexpr uint8_t kSecLevelMask = 7 << 0;
|
||||
static constexpr uint8_t kKeyIdModeMask = 3 << 3;
|
||||
@@ -1144,6 +1164,12 @@ protected:
|
||||
|
||||
static bool IsDstAddrPresent(uint16_t aFcf) { return (aFcf & kFcfDstAddrMask) != kFcfDstAddrNone; }
|
||||
static bool IsDstPanIdPresent(uint16_t aFcf);
|
||||
static bool IsSequenceSuppressed(uint16_t aFcf)
|
||||
{
|
||||
return (aFcf & (kFcfSequenceSupression | kFcfFrameVersionMask)) == (kFcfSequenceSupression | kVersion2015);
|
||||
}
|
||||
static uint8_t GetSeqNumSize(uint16_t aFcf) { return !IsSequenceSuppressed(aFcf) ? kDsnSize : 0; }
|
||||
|
||||
static bool IsSrcAddrPresent(uint16_t aFcf) { return (aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone; }
|
||||
static bool IsSrcPanIdPresent(uint16_t aFcf);
|
||||
static bool IsVersion2015(uint16_t aFcf) { return (aFcf & kFcfFrameVersionMask) == kVersion2015; }
|
||||
|
||||
@@ -77,9 +77,21 @@ bool otMacFrameIsAckRequested(const otRadioFrame *aFrame)
|
||||
return static_cast<const Mac::Frame *>(aFrame)->GetAckRequest();
|
||||
}
|
||||
|
||||
uint8_t otMacFrameGetSequence(const otRadioFrame *aFrame)
|
||||
otError otMacFrameGetSequence(const otRadioFrame *aFrame, uint8_t *aSequence)
|
||||
{
|
||||
return static_cast<const Mac::Frame *>(aFrame)->GetSequence();
|
||||
otError error;
|
||||
|
||||
if (static_cast<const Mac::Frame *>(aFrame)->IsSequencePresent())
|
||||
{
|
||||
*aSequence = static_cast<const Mac::Frame *>(aFrame)->GetSequence();
|
||||
error = kErrorNone;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = kErrorParse;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void FuzzerPlatformInit(void)
|
||||
@@ -101,10 +113,13 @@ void FuzzerPlatformProcess(otInstance *aInstance)
|
||||
|
||||
if (otMacFrameIsAckRequested(&sRadioTransmitFrame))
|
||||
{
|
||||
otError error;
|
||||
|
||||
sRadioAckFrame.mLength = IEEE802154_ACK_LENGTH;
|
||||
sRadioAckFrame.mPsdu[0] = IEEE802154_FRAME_TYPE_ACK;
|
||||
sRadioAckFrame.mPsdu[1] = 0;
|
||||
sRadioAckFrame.mPsdu[2] = otMacFrameGetSequence(&sRadioTransmitFrame);
|
||||
error = otMacFrameGetSequence(&sRadioTransmitFrame, &sRadioAckFrame.mPsdu[2]);
|
||||
OT_ASSERT(error == OT_ERROR_NONE);
|
||||
sRadioAckFrame.mChannel = sRadioTransmitFrame.mChannel;
|
||||
|
||||
otPlatRadioTxDone(aInstance, &sRadioTransmitFrame, &sRadioAckFrame, OT_ERROR_NONE);
|
||||
|
||||
@@ -205,6 +205,7 @@ void TestMacHeader(void)
|
||||
Mac::Frame::KeyIdMode mKeyIdMode;
|
||||
uint8_t mHeaderLength;
|
||||
uint8_t mFooterLength;
|
||||
bool mSuppressSequence;
|
||||
};
|
||||
|
||||
static constexpr Mac::Frame::Version kVer2006 = Mac::Frame::kVersion2006;
|
||||
@@ -265,6 +266,7 @@ void TestMacHeader(void)
|
||||
{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;
|
||||
@@ -285,7 +287,9 @@ void TestMacHeader(void)
|
||||
|
||||
for (const TestCase &testCase : kTestCases)
|
||||
{
|
||||
uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE];
|
||||
uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE];
|
||||
uint8_t offset;
|
||||
|
||||
Mac::TxFrame frame;
|
||||
Mac::Addresses addresses;
|
||||
Mac::Address address;
|
||||
@@ -352,7 +356,7 @@ void TestMacHeader(void)
|
||||
}
|
||||
|
||||
frame.InitMacHeader(Mac::Frame::kTypeData, testCase.mVersion, addresses, panIds, testCase.mSecurity,
|
||||
testCase.mKeyIdMode);
|
||||
testCase.mKeyIdMode, testCase.mSuppressSequence);
|
||||
|
||||
VerifyOrQuit(frame.GetHeaderLength() == testCase.mHeaderLength);
|
||||
VerifyOrQuit(frame.GetFooterLength() == testCase.mFooterLength);
|
||||
@@ -401,11 +405,20 @@ void TestMacHeader(void)
|
||||
VerifyOrQuit(keyIdMode == testCase.mKeyIdMode);
|
||||
}
|
||||
|
||||
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");
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -590,6 +603,7 @@ void TestMacFrameApi(void)
|
||||
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)
|
||||
@@ -619,6 +633,7 @@ void TestMacFrameApi(void)
|
||||
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);
|
||||
@@ -637,6 +652,7 @@ void TestMacFrameApi(void)
|
||||
// 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);
|
||||
@@ -700,6 +716,7 @@ void TestMacFrameAckGeneration(void)
|
||||
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)
|
||||
@@ -758,6 +775,7 @@ void TestMacFrameAckGeneration(void)
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user