Files
Abtin Keshavarzian 02aaf7cb73 [tlv] remove Tlv::ReadTlvValue() (#12768)
This commit removes the `Tlv::ReadTlvValue()` method which is no
longer needed or used. The same functionality is provided through
the `Tlv::Info` class, specifically using its `ParseFrom()` and
`ReadValue()` methods. This approach is safer and provides more
comprehensive information about the parsed TLV.

The unit tests are also updated to replace the usage of the removed
method with the new `Tlv::Info` based approach.
2026-03-25 23:33:41 -05:00

499 lines
18 KiB
C++

/*
* Copyright (c) 2022, 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 "test_platform.h"
#include <openthread/config.h>
#include "common/message.hpp"
#include "common/tlvs.hpp"
#include "instance/instance.hpp"
#include "test_util.h"
#include "test_util.hpp"
namespace ot {
void TestTlv(void)
{
static constexpr uint16_t kMaxBufferSize = 300;
Instance *instance = testInitInstance();
Message *message;
Tlv tlv;
Tlv::Info tlvInfo;
ExtendedTlv extTlv;
Tlv::Bookmark bookmark;
uint16_t offset;
OffsetRange offsetRange;
uint16_t length;
uint16_t prevLength;
uint16_t index;
uint8_t buffer[kMaxBufferSize];
VerifyOrQuit(instance != nullptr);
VerifyOrQuit((message = instance->Get<MessagePool>().Allocate(Message::kTypeIp6)) != nullptr);
VerifyOrQuit(message != nullptr);
VerifyOrQuit(message->GetOffset() == 0);
VerifyOrQuit(message->GetLength() == 0);
VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 1, offsetRange) == kErrorNotFound);
VerifyOrQuit(tlvInfo.ParseFrom(*message, 0) == kErrorParse);
// Add an empty TLV with type 1 and check that we can find it
offset = message->GetLength();
tlv.SetType(1);
tlv.SetLength(0);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 1, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == sizeof(Tlv));
VerifyOrQuit(offsetRange.GetLength() == 0);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetType() == 1);
VerifyOrQuit(tlvInfo.GetLength() == 0);
VerifyOrQuit(!tlvInfo.IsExtended());
VerifyOrQuit(tlvInfo.ReadValue(*message, buffer, 1) == kErrorParse);
// Add an empty extended TLV (type 2), and check that we can find it.
offset = message->GetLength();
extTlv.SetType(2);
extTlv.SetLength(0);
SuccessOrQuit(message->Append(extTlv));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 2, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(offsetRange.GetLength() == 0);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetType() == 2);
VerifyOrQuit(tlvInfo.GetLength() == 0);
VerifyOrQuit(tlvInfo.IsExtended());
VerifyOrQuit(tlvInfo.ReadValue(*message, buffer, 1) == kErrorParse);
// Add a TLV with type 3 with one byte value and check if we can find it.
offset = message->GetLength();
tlv.SetType(3);
tlv.SetLength(1);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(message->Append<uint8_t>(0xff));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 3, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(Tlv));
VerifyOrQuit(offsetRange.GetLength() == 1);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetType() == 3);
VerifyOrQuit(tlvInfo.GetLength() == 1);
VerifyOrQuit(!tlvInfo.IsExtended());
SuccessOrQuit(tlvInfo.ReadValue(*message, buffer, 1));
VerifyOrQuit(buffer[0] == 0x0ff);
VerifyOrQuit(tlvInfo.ReadValue(*message, buffer, 2) == kErrorParse);
// Add an extended TLV with type 4 with two byte value and check if we can find it.
offset = message->GetLength();
extTlv.SetType(4);
extTlv.SetLength(2);
SuccessOrQuit(message->Append(extTlv));
SuccessOrQuit(message->Append<uint8_t>(0x12));
SuccessOrQuit(message->Append<uint8_t>(0x34));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 4, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(offsetRange.GetLength() == 2);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetType() == 4);
VerifyOrQuit(tlvInfo.GetLength() == 2);
VerifyOrQuit(tlvInfo.IsExtended());
SuccessOrQuit(tlvInfo.ReadValue(*message, buffer, 1));
VerifyOrQuit(buffer[0] == 0x12);
SuccessOrQuit(tlvInfo.ReadValue(*message, buffer, 2));
VerifyOrQuit(buffer[0] == 0x12);
VerifyOrQuit(buffer[1] == 0x34);
VerifyOrQuit(tlvInfo.ReadValue(*message, buffer, 3) == kErrorParse);
// Add a TLV with missing value.
offset = message->GetLength();
tlv.SetType(5);
tlv.SetLength(1);
SuccessOrQuit(message->Append(tlv));
VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 5, offsetRange) != kErrorNone);
VerifyOrQuit(tlvInfo.ParseFrom(*message, offset) == kErrorParse);
// Add the missing value.
SuccessOrQuit(message->Append<uint8_t>(0xaa));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 5, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(Tlv));
VerifyOrQuit(offsetRange.GetLength() == 1);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetType() == 5);
VerifyOrQuit(tlvInfo.GetLength() == 1);
VerifyOrQuit(!tlvInfo.IsExtended());
SuccessOrQuit(tlvInfo.ReadValue(*message, buffer, 1));
VerifyOrQuit(buffer[0] == 0xaa);
VerifyOrQuit(tlvInfo.ReadValue(*message, buffer, 2) == kErrorParse);
// Add an extended TLV with missing value.
offset = message->GetLength();
extTlv.SetType(6);
extTlv.SetLength(2);
SuccessOrQuit(message->Append(extTlv));
SuccessOrQuit(message->Append<uint8_t>(0xbb));
VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 6, offsetRange) != kErrorNone);
VerifyOrQuit(tlvInfo.ParseFrom(*message, offset) == kErrorParse);
SuccessOrQuit(message->Append<uint8_t>(0xcc));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 6, offsetRange) != kErrorNone);
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(offsetRange.GetLength() == 2);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetType() == 6);
VerifyOrQuit(tlvInfo.GetLength() == 2);
VerifyOrQuit(tlvInfo.IsExtended());
SuccessOrQuit(tlvInfo.ReadValue(*message, buffer, 2));
VerifyOrQuit(buffer[0] == 0xbb);
VerifyOrQuit(buffer[1] == 0xcc);
VerifyOrQuit(tlvInfo.ReadValue(*message, buffer, 3) == kErrorParse);
// Add an extended TLV with overflow length.
offset = message->GetLength();
extTlv.SetType(7);
extTlv.SetLength(0xffff);
SuccessOrQuit(message->Append(extTlv));
SuccessOrQuit(message->Append<uint8_t>(0x11));
VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 7, offsetRange) != kErrorNone);
VerifyOrQuit(tlvInfo.ParseFrom(*message, offset) == kErrorParse);
//- - - - - - - - - - - - - - - - - - - - - -
// Validate `StartTlv()`, `AdjustTlv()`, `EndTlv()`
SuccessOrQuit(message->SetLength(0));
offset = 0;
// Build a TLV with length 3
SuccessOrQuit(Tlv::StartTlv(*message, /* aType */ 1, bookmark));
SuccessOrQuit(message->Append<uint8_t>(0xab));
SuccessOrQuit(message->Append<uint8_t>(0xcd));
SuccessOrQuit(message->Append<uint8_t>(0xef));
SuccessOrQuit(Tlv::EndTlv(*message, bookmark));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 1, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(Tlv));
VerifyOrQuit(offsetRange.GetLength() == 3);
SuccessOrQuit(tlvInfo.ParseFrom(*message, offset));
VerifyOrQuit(tlvInfo.GetLength() == 3);
VerifyOrQuit(!tlvInfo.IsExtended());
SuccessOrQuit(tlvInfo.ReadValue(*message, buffer, 3));
VerifyOrQuit(buffer[0] == 0xab);
VerifyOrQuit(buffer[1] == 0xcd);
VerifyOrQuit(buffer[2] == 0xef);
offset = offsetRange.GetEndOffset();
VerifyOrQuit(offset == message->GetLength());
for (index = 0; index < kMaxBufferSize; index++)
{
buffer[index] = static_cast<uint8_t>(index);
}
// Build a TLV with length 254 (max for a regular TLV).
SuccessOrQuit(Tlv::StartTlv(*message, /* aType */ 2, bookmark));
SuccessOrQuit(message->AppendBytes(buffer, Tlv::kBaseTlvMaxLength));
SuccessOrQuit(Tlv::EndTlv(*message, bookmark));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 2, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(Tlv));
VerifyOrQuit(offsetRange.GetLength() == Tlv::kBaseTlvMaxLength);
VerifyOrQuit(message->CompareBytes(offsetRange, buffer));
offset = offsetRange.GetEndOffset();
VerifyOrQuit(offset == message->GetLength());
// Build a TLV with length 255 (ensure it is written as Extended TLV).
SuccessOrQuit(Tlv::StartTlv(*message, /* aType */ 3, bookmark));
SuccessOrQuit(message->AppendBytes(buffer, Tlv::kBaseTlvMaxLength + 1));
SuccessOrQuit(Tlv::EndTlv(*message, bookmark));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 3, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(offsetRange.GetLength() == Tlv::kBaseTlvMaxLength + 1);
VerifyOrQuit(message->CompareBytes(offsetRange, buffer));
offset = offsetRange.GetEndOffset();
VerifyOrQuit(offset == message->GetLength());
// Validate that `AdjustTlv()` copies the bytes only when we reach the
// TLV length limit.
SuccessOrQuit(Tlv::StartTlv(*message, /* aType */ 4, bookmark));
for (index = 0; index < Tlv::kBaseTlvMaxLength; index++)
{
SuccessOrQuit(message->Append<uint8_t>(buffer[index]));
prevLength = message->GetLength();
SuccessOrQuit(Tlv::AdjustTlv(*message, bookmark));
VerifyOrQuit(prevLength == message->GetLength());
}
SuccessOrQuit(message->Append<uint8_t>(buffer[index]));
index++;
prevLength = message->GetLength();
SuccessOrQuit(Tlv::AdjustTlv(*message, bookmark));
VerifyOrQuit(message->GetLength() == prevLength + sizeof(uint16_t));
for (; index < kMaxBufferSize; index++)
{
SuccessOrQuit(message->Append<uint8_t>(buffer[index]));
prevLength = message->GetLength();
SuccessOrQuit(Tlv::AdjustTlv(*message, bookmark));
VerifyOrQuit(prevLength == message->GetLength());
}
SuccessOrQuit(Tlv::EndTlv(*message, bookmark));
SuccessOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 4, offsetRange));
VerifyOrQuit(offsetRange.GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(offsetRange.GetLength() == kMaxBufferSize);
VerifyOrQuit(message->CompareBytes(offsetRange, buffer));
offset = offsetRange.GetEndOffset();
VerifyOrQuit(offset == message->GetLength());
message->Free();
testFreeInstance(instance);
}
void TestTlvInfo(void)
{
Instance *instance;
Message *message;
uint16_t offset;
uint16_t len;
Tlv tlv;
ExtendedTlv extTlv;
Tlv::Info info;
instance = testInitInstance();
VerifyOrQuit(instance != nullptr);
message = instance->Get<MessagePool>().Allocate(Message::kTypeOther);
VerifyOrQuit(message != nullptr);
// Append TLV 1: Standard TLV with 1-byte value.
tlv.SetType(1);
tlv.SetLength(1);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(message->Append<uint8_t>(0xaa));
// Append TLV 2: Extended TLV with 2-byte value.
extTlv.SetType(2);
extTlv.SetLength(2);
SuccessOrQuit(message->Append(extTlv));
SuccessOrQuit(message->Append<uint16_t>(0xcafe));
// Append TLV 3: Standard empty TLV.
tlv.SetType(3);
tlv.SetLength(0);
SuccessOrQuit(message->Append(tlv));
// Append TLV 4: Extended empty TLV.
extTlv.SetType(4);
extTlv.SetLength(0);
SuccessOrQuit(message->Append(extTlv));
// Append TLV 5: Malformed standard TLV (claims length 2, but has only 1).
tlv.SetType(5);
tlv.SetLength(2);
SuccessOrQuit(message->Append(tlv));
SuccessOrQuit(message->Append<uint8_t>(0x12));
for (uint8_t testIter = 0; testIter <= 1; testIter++)
{
offset = 0;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TLV 1 (standard, len=1)
len = 1;
if (testIter == 0)
{
SuccessOrQuit(info.ParseFrom(*message, offset));
}
else
{
SuccessOrQuit(info.FindIn(*message, 1));
}
VerifyOrQuit(info.GetType() == 1);
VerifyOrQuit(info.GetLength() == len);
VerifyOrQuit(!info.IsExtended());
VerifyOrQuit(info.GetSize() == sizeof(Tlv) + len);
VerifyOrQuit(info.GetTlvOffset() == offset);
VerifyOrQuit(info.GetValueOffset() == offset + sizeof(Tlv));
VerifyOrQuit(info.GetTlvOffsetRange().GetOffset() == offset);
VerifyOrQuit(info.GetTlvOffsetRange().GetLength() == sizeof(Tlv) + len);
VerifyOrQuit(info.GetValueOffsetRange().GetOffset() == offset + sizeof(Tlv));
VerifyOrQuit(info.GetValueOffsetRange().GetLength() == len);
offset = info.GetTlvOffsetRange().GetEndOffset();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TLV 2 (extended, len=2)
len = 2;
if (testIter == 0)
{
SuccessOrQuit(info.ParseFrom(*message, offset));
}
else
{
SuccessOrQuit(info.FindIn(*message, 2));
}
VerifyOrQuit(info.GetType() == 2);
VerifyOrQuit(info.GetLength() == len);
VerifyOrQuit(info.IsExtended());
VerifyOrQuit(info.GetSize() == sizeof(ExtendedTlv) + len);
VerifyOrQuit(info.GetTlvOffset() == offset);
VerifyOrQuit(info.GetValueOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(info.GetTlvOffsetRange().GetOffset() == offset);
VerifyOrQuit(info.GetTlvOffsetRange().GetLength() == sizeof(ExtendedTlv) + len);
VerifyOrQuit(info.GetValueOffsetRange().GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(info.GetValueOffsetRange().GetLength() == len);
offset = info.GetTlvOffsetRange().GetEndOffset();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TLV 3 (standard, empty)
len = 0;
if (testIter == 0)
{
SuccessOrQuit(info.ParseFrom(*message, offset));
}
else
{
SuccessOrQuit(info.FindIn(*message, 3));
}
VerifyOrQuit(info.GetType() == 3);
VerifyOrQuit(info.GetLength() == len);
VerifyOrQuit(!info.IsExtended());
VerifyOrQuit(info.GetSize() == sizeof(Tlv) + len);
VerifyOrQuit(info.GetTlvOffset() == offset);
VerifyOrQuit(info.GetValueOffset() == offset + sizeof(Tlv));
VerifyOrQuit(info.GetTlvOffsetRange().GetOffset() == offset);
VerifyOrQuit(info.GetTlvOffsetRange().GetLength() == sizeof(Tlv) + len);
VerifyOrQuit(info.GetValueOffsetRange().GetOffset() == offset + sizeof(Tlv));
VerifyOrQuit(info.GetValueOffsetRange().GetLength() == len);
offset = info.GetTlvOffsetRange().GetEndOffset();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TLV 4 (extended, empty)
len = 0;
if (testIter == 0)
{
SuccessOrQuit(info.ParseFrom(*message, offset));
}
else
{
SuccessOrQuit(info.FindIn(*message, 4));
}
VerifyOrQuit(info.GetType() == 4);
VerifyOrQuit(info.GetLength() == len);
VerifyOrQuit(info.IsExtended());
VerifyOrQuit(info.GetSize() == sizeof(ExtendedTlv) + len);
VerifyOrQuit(info.GetTlvOffset() == offset);
VerifyOrQuit(info.GetValueOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(info.GetTlvOffsetRange().GetOffset() == offset);
VerifyOrQuit(info.GetTlvOffsetRange().GetLength() == sizeof(ExtendedTlv) + len);
VerifyOrQuit(info.GetValueOffsetRange().GetOffset() == offset + sizeof(ExtendedTlv));
VerifyOrQuit(info.GetValueOffsetRange().GetLength() == len);
offset = info.GetTlvOffsetRange().GetEndOffset();
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Test TLV 5 (malformed)
if (testIter == 0)
{
VerifyOrQuit(info.ParseFrom(*message, offset) == kErrorParse);
}
else
{
VerifyOrQuit(info.FindIn(*message, 5) != kErrorNone);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
message->Free();
testFreeInstance(instance);
}
} // namespace ot
int main(void)
{
ot::TestTlv();
ot::TestTlvInfo();
printf("All tests passed\n");
return 0;
}