[tlv] handle Extended TLV and simplify searching for TLV in a message (#4152)

This commit contains the following change in `Tlv` class:

- Changes `GetSize()`, `GetValue()` and `GetNext()` to ensure they
  work correctly independent of whether the TLV is an Extended TLV
  or not.
- Defines a new common private static method `Tlv::Find()` which
  is then used to simplify `Tlv::Get()` and `Tlv::GetOffset()` and
  `Tlv::GetValueOffset()` implementations.
- This change also ensures `Tlv::Get()` (which finds and reads a
  TLV of a given type within a message) work correctly for Extended
  TLVs.
This commit is contained in:
Abtin Keshavarzian
2019-09-12 09:30:22 -07:00
committed by Jonathan Hui
parent 0e489b07e2
commit 3514458d5d
2 changed files with 143 additions and 69 deletions
+83 -57
View File
@@ -38,20 +38,36 @@
namespace ot {
otError Tlv::Get(const Message &aMessage, uint8_t aType, uint16_t aMaxLength, Tlv &aTlv)
uint16_t Tlv::GetSize(void) const
{
otError error = OT_ERROR_NOT_FOUND;
return IsExtended() ? sizeof(ExtendedTlv) + static_cast<const ExtendedTlv *>(this)->GetLength()
: sizeof(Tlv) + GetLength();
}
uint8_t *Tlv::GetValue(void)
{
return reinterpret_cast<uint8_t *>(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv));
}
const uint8_t *Tlv::GetValue(void) const
{
return reinterpret_cast<const uint8_t *>(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv));
}
otError Tlv::Get(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv)
{
otError error;
uint16_t offset;
uint16_t size;
SuccessOrExit(error = GetOffset(aMessage, aType, offset));
aMessage.Read(offset, sizeof(Tlv), &aTlv);
SuccessOrExit(error = Find(aMessage, aType, &offset, &size, NULL));
if (aMaxLength > sizeof(aTlv) + aTlv.GetLength())
if (aMaxSize > size)
{
aMaxLength = sizeof(aTlv) + aTlv.GetLength();
aMaxSize = size;
}
aMessage.Read(offset, aMaxLength, &aTlv);
aMessage.Read(offset, aMaxSize, &aTlv);
exit:
return error;
@@ -59,77 +75,87 @@ exit:
otError Tlv::GetOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset)
{
otError error = OT_ERROR_NOT_FOUND;
uint16_t offset = aMessage.GetOffset();
uint16_t end = aMessage.GetLength();
Tlv tlv;
return Find(aMessage, aType, &aOffset, NULL, NULL);
}
while (offset + sizeof(tlv) <= end)
otError Tlv::GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength)
{
otError error;
uint16_t offset;
uint16_t size;
bool isExtendedTlv;
SuccessOrExit(error = Find(aMessage, aType, &offset, &size, &isExtendedTlv));
if (!isExtendedTlv)
{
uint32_t length = sizeof(tlv);
aMessage.Read(offset, sizeof(tlv), &tlv);
if (tlv.GetLength() != kExtendedLength)
{
length += tlv.GetLength();
}
else
{
uint16_t extLength;
VerifyOrExit(sizeof(extLength) == aMessage.Read(offset + sizeof(tlv), sizeof(extLength), &extLength));
length += sizeof(extLength) + HostSwap16(extLength);
}
VerifyOrExit(offset + length <= end);
if (tlv.GetType() == aType)
{
aOffset = offset;
ExitNow(error = OT_ERROR_NONE);
}
offset += static_cast<uint16_t>(length);
aValueOffset = offset + sizeof(Tlv);
aLength = size - sizeof(Tlv);
}
else
{
aValueOffset = offset + sizeof(ExtendedTlv);
aLength = size - sizeof(ExtendedTlv);
}
exit:
return error;
}
otError Tlv::GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset, uint16_t &aLength)
otError Tlv::Find(const Message &aMessage, uint8_t aType, uint16_t *aOffset, uint16_t *aSize, bool *aIsExtendedTlv)
{
otError error = OT_ERROR_NOT_FOUND;
uint16_t offset = aMessage.GetOffset();
uint16_t end = aMessage.GetLength();
otError error = OT_ERROR_NOT_FOUND;
uint16_t offset = aMessage.GetOffset();
uint16_t remainingLen = aMessage.GetLength();
Tlv tlv;
uint16_t size;
while (offset + sizeof(tlv) <= end)
VerifyOrExit(offset <= remainingLen);
remainingLen -= offset;
while (true)
{
uint16_t length;
VerifyOrExit(sizeof(Tlv) <= remainingLen);
aMessage.Read(offset, sizeof(Tlv), &tlv);
aMessage.Read(offset, sizeof(tlv), &tlv);
offset += sizeof(tlv);
length = tlv.GetLength();
if (length == kExtendedLength)
if (tlv.mLength != kExtendedLength)
{
VerifyOrExit(offset + sizeof(length) <= end);
aMessage.Read(offset, sizeof(length), &length);
offset += sizeof(length);
length = HostSwap16(length);
size = tlv.GetSize();
}
else
{
ExtendedTlv extTlv;
VerifyOrExit(sizeof(ExtendedTlv) <= remainingLen);
aMessage.Read(offset, sizeof(ExtendedTlv), &extTlv);
size = extTlv.GetSize();
}
VerifyOrExit(length <= end - offset);
VerifyOrExit(size <= remainingLen);
if (tlv.GetType() == aType)
{
aOffset = offset;
aLength = length;
ExitNow(error = OT_ERROR_NONE);
if (aOffset != NULL)
{
*aOffset = offset;
}
if (aSize != NULL)
{
*aSize = size;
}
if (aIsExtendedTlv != NULL)
{
*aIsExtendedTlv = (tlv.mLength == kExtendedLength);
}
error = OT_ERROR_NONE;
ExitNow();
}
offset += length;
offset += size;
remainingLen -= size;
}
exit:
+60 -12
View File
@@ -83,9 +83,22 @@ public:
*/
void SetType(uint8_t aType) { mType = aType; }
/**
* This method indicates whether the TLV is an Extended TLV.
*
* @retval TRUE If the TLV is an Extended TLV.
* @retval FALSE If the TLV is not an Extended TLV.
*
*/
bool IsExtended(void) const { return (mLength == kExtendedLength); }
/**
* This method returns the Length value.
*
* @note This method should be used when TLV is not an Extended TLV, otherwise the returned length from this method
* would not be correct. When TLV is an Extended TLV, the TLV should be down-casted to the `ExtendedTlv` type and
* the `ExtendedTlv::GetLength()` should be used instead.
*
* @returns The Length value.
*
*/
@@ -100,65 +113,79 @@ public:
void SetLength(uint8_t aLength) { mLength = aLength; }
/**
* This method returns the total size including Type, Length, and Value fields.
* This method returns the TLV's total size (number of bytes) including Type, Length, and Value fields.
*
* This method correctly returns the TLV size independent of whether the TLV is an Extended TLV or not.
*
* @returns The total size include Type, Length, and Value fields.
*
*/
uint16_t GetSize(void) const { return sizeof(Tlv) + mLength; }
uint16_t GetSize(void) const;
/**
* This method returns a pointer to the Value.
*
* This method can be used independent of whether the TLV is an Extended TLV or not.
*
* @returns A pointer to the value.
*
*/
uint8_t *GetValue(void) { return reinterpret_cast<uint8_t *>(this) + sizeof(Tlv); }
uint8_t *GetValue(void);
/**
* This method returns a pointer to the Value.
*
* This method can be used independent of whether the TLV is an Extended TLV or not.
*
* @returns A pointer to the value.
*
*/
const uint8_t *GetValue(void) const { return reinterpret_cast<const uint8_t *>(this) + sizeof(Tlv); }
const uint8_t *GetValue(void) const;
/**
* This method returns a pointer to the next TLV.
*
* This method correctly returns the next TLV independent of whether the current TLV is an Extended TLV or not.
*
* @returns A pointer to the next TLV.
*
*/
Tlv *GetNext(void) { return reinterpret_cast<Tlv *>(reinterpret_cast<uint8_t *>(this) + sizeof(*this) + mLength); }
Tlv *GetNext(void) { return reinterpret_cast<Tlv *>(reinterpret_cast<uint8_t *>(this) + GetSize()); }
/**
* This method returns a pointer to the next TLV.
*
* This method correctly returns the next TLV independent of whether the current TLV is an Extended TLV or not.
*
* @returns A pointer to the next TLV.
*
*/
const Tlv *GetNext(void) const
{
return reinterpret_cast<const Tlv *>(reinterpret_cast<const uint8_t *>(this) + sizeof(*this) + mLength);
return reinterpret_cast<const Tlv *>(reinterpret_cast<const uint8_t *>(this) + GetSize());
}
/**
* This static method reads the requested TLV out of @p aMessage.
*
* This method can be used independent of whether the read TLV (from message) is an Extended TLV or not.
*
* @param[in] aMessage A reference to the message.
* @param[in] aType The Type value to search for.
* @param[in] aMaxLength Maximum number of bytes to read.
* @param[in] aMaxSize Maximum number of bytes to read.
* @param[out] aTlv A reference to the TLV that will be copied to.
*
* @retval OT_ERROR_NONE Successfully copied the TLV.
* @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType.
*
*/
static otError Get(const Message &aMessage, uint8_t aType, uint16_t aMaxLength, Tlv &aTlv);
static otError Get(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv);
/**
* This static method obtains the offset of a TLV within @p aMessage.
*
* This method can be used independent of whether the read TLV (from message) is an Extended TLV or not.
*
* @param[in] aMessage A reference to the message.
* @param[in] aType The Type value to search for.
* @param[out] aOffset A reference to the offset of the TLV.
@@ -172,6 +199,8 @@ public:
/**
* This static method finds the offset and length of a given TLV type.
*
* This method can be used independent of whether the read TLV (from message) is an Extended TLV or not.
*
* @param[in] aMessage A reference to the message.
* @param[in] aType The Type value to search for.
* @param[out] aOffset The offset where the value starts.
@@ -184,16 +213,35 @@ public:
static otError GetValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset, uint16_t &aLength);
protected:
/**
* Length values.
*
*/
enum
{
kExtendedLength = 255, ///< Extended Length value
};
private:
/**
* This private static method searches within a given message for TLV type and outputs the TLV offset, size and
* whether it is an Extended TLV.
*
* A NULL pointer can be used for output parameters @p aOffset, @p aSize, or @p aIsExtendedTlv if the parameter
* is not required.
*
* @param[in] aMessage A reference to the message to search within.
* @param[in] aType The TLV type to search for.
* @param[out] aOffset A pointer to a variable to output the offset to the start of the TLV.
* @param[out] aSize A pointer to a variable to output the size (total number of bytes) of the TLV.
* @param[out] aIsExtendedTlv A pointer to a boolean variable to output whether the found TLV is extended or not.
*
* @retval OT_ERROR_NONE Successfully found the TLV.
* @retval OT_ERROR_NOT_FOUND Could not find the TLV with Type @p aType.
*
*/
static otError Find(const Message &aMessage,
uint8_t aType,
uint16_t * aOffset,
uint16_t * aSize,
bool * aIsExtendedTlv);
uint8_t mType;
uint8_t mLength;
} OT_TOOL_PACKED_END;