[tlvs] add Start/End/AdjustTlv methods for staged writing (#12250)

This commit introduces a new set of static methods to simplify
writing TLVs with variable lengths to a `Message`.

The new mechanism consists of three methods:
- `Tlv::StartTlv()`: Appends a placeholder TLV header and returns a
  `Bookmark`.
- `Tlv::AdjustTlv()`: Optionally promotes the TLV to an extended TLV
  if the length grows beyond the standard TLV limit. This is an
  optimization to avoid large copies within a message.
- `Tlv::EndTlv()`: Calculates the final length and updates the TLV
  header, promoting to an extended TLV if necessary.

This new set replaces the common but cumbersome pattern of manually
saving the start offset, appending data, and then back-patching the
length field.

The existing code is updated to use this new, simpler, and more
robust mechanism.

This commit also adds unit tests to validate the new functionality.
This commit is contained in:
Abtin Keshavarzian
2025-12-31 14:48:00 -08:00
committed by GitHub
parent b4ad385737
commit da1f792770
9 changed files with 327 additions and 187 deletions
+76
View File
@@ -308,6 +308,82 @@ exit:
return error;
}
Error Tlv::StartTlv(Message &aMessage, uint8_t aType, Bookmark &aBookmark)
{
Tlv tlv;
tlv.SetType(aType);
tlv.SetLength(0);
aBookmark = aMessage.GetLength();
return aMessage.Append(tlv);
}
Error Tlv::AdjustTlv(Message &aMessage, Bookmark aBookmark)
{
return UpdateTlv(aMessage, aBookmark, /* aShouldWriteLength */ false);
}
Error Tlv::EndTlv(Message &aMessage, Bookmark aBookmark)
{
return UpdateTlv(aMessage, aBookmark, /* aShouldWriteLength */ true);
}
Error Tlv::UpdateTlv(Message &aMessage, Bookmark aBookmark, bool aShouldWriteLength)
{
Error error;
uint16_t startOffset = aBookmark;
uint16_t length;
Tlv tlv;
ExtendedTlv extTlv;
SuccessOrExit(error = aMessage.Read(startOffset, tlv));
length = aMessage.GetLength() - startOffset;
if (tlv.IsExtended())
{
length -= sizeof(ExtendedTlv);
}
else
{
length -= sizeof(Tlv);
if (length > kBaseTlvMaxLength)
{
// If the TLV is not already an Extended TLV, change it. We
// need to move the written value bytes forward to make
// room for the Extended TLV header.
SuccessOrExit(error = aMessage.SetLength(aMessage.GetLength() + sizeof(ExtendedTlv) - sizeof(Tlv)));
aMessage.WriteBytesFromMessage(/* aWriteOffset */ startOffset + sizeof(ExtendedTlv), aMessage,
/* aReadOffset */ startOffset + sizeof(Tlv), length);
tlv.SetLength(kExtendedLength);
aMessage.Write(startOffset, tlv);
}
}
VerifyOrExit(aShouldWriteLength);
if (!tlv.IsExtended())
{
tlv.SetLength(static_cast<uint8_t>(length));
aMessage.Write(startOffset, tlv);
}
else
{
extTlv.SetType(tlv.GetType());
extTlv.SetLength(length);
aMessage.Write(startOffset, extTlv);
}
exit:
return error;
}
const Tlv *Tlv::FindTlv(const void *aTlvsStart, uint16_t aTlvsLength, uint8_t aType)
{
const Tlv *tlv;
+58
View File
@@ -655,6 +655,63 @@ public:
return ValidateStringTlvValue(StringTlvType::kMaxStringLength, aValue);
}
//------------------------------------------------------------------------------------------------------------------
// Static methods for writing variable length TLVs in a `Message`.
/**
* Represents the opaque type for a bookmark used by `StartTlv()`, `AdjustTlv()`, and `EndTlv()`.
*/
typedef uint16_t Bookmark;
/**
* Starts appending a new TLV to a message.
*
* This method is used in conjunction with `AdjustTlv()` and `EndTlv()` to append a TLV where the length is not
* known in advance. `StartTlv()` writes a placeholder TLV header and records its position in @p aBookmark.
* The caller can then append the value of the TLV to the message and finalize the TLV by calling `EndTlv()`.
*
* @param[in] aMessage The message to append the TLV to.
* @param[in] aType The type of the TLV.
* @param[out] aBookmark A reference to a `Bookmark` to store the position of the new TLV.
*
* @retval kErrorNone Successfully started the TLV by appending a placeholder header.
* @retval kErrorNoBufs Insufficient space to append the placeholder header.
*/
static Error StartTlv(Message &aMessage, uint8_t aType, Bookmark &aBookmark);
/**
* Adjusts a TLV header during a staged append, promoting it to an extended TLV if needed.
*
* This method can be called periodically while appending a large TLV value (after a `StartTlv()` call). It checks
* if the current length of the TLV has exceeded the capacity of a standard TLV. If so, it "promotes" the header to
* an extended TLV by shifting the already-written value data to make room for the larger header. This avoids a
* potentially large memory copy operation in the final `EndTlv()` call.
*
* This method is optional and intended as a performance optimization for large TLVs.
*
* @param[in] aMessage The message containing the TLV.
* @param[in] aBookmark The bookmark from the `StartTlv()` call.
*
* @retval kErrorNone The TLV header was either successfully promoted or did not require promotion.
* @retval kErrorNoBufs Insufficient space to promote the header.
*/
static Error AdjustTlv(Message &aMessage, Bookmark aBookmark);
/**
* Finalizes a TLV that was started by `StartTlv()`.
*
* This method calculates the final length of the TLV value appended after the `StartTlv()` call and writes the
* correct length into the TLV header. If the final length requires an extended TLV and the header has not
* already been promoted by `AdjustTlv()`, this method will handle the promotion.
*
* @param[in] aMessage The message containing the TLV.
* @param[in] aBookmark The bookmark from the `StartTlv()` call.
*
* @retval kErrorNone Successfully finalized the TLV.
* @retval kErrorNoBufs Insufficient space if header promotion is required.
*/
static Error EndTlv(Message &aMessage, Bookmark aBookmark);
//------------------------------------------------------------------------------------------------------------------
// Static methods for finding TLVs within a sequence of TLVs.
@@ -722,6 +779,7 @@ private:
static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue);
static Error AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue);
static Error ValidateStringTlvValue(uint8_t aMaxStringLength, const char *aStringValue);
static Error UpdateTlv(Message &aMessage, Bookmark aBookmark, bool aShouldWriteLength);
template <typename UintType> static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue);
template <typename UintType> static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue);
template <typename UintType> static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue);
+7 -15
View File
@@ -979,10 +979,8 @@ exit:
Error TcatAgent::HandleGetApplicationLayers(Message &aOutgoingMessage, bool &aResponse)
{
Error error = kErrorNone;
ot::Tlv tlv;
uint8_t replyLen = 0;
uint8_t count = 0;
Error error = kErrorNone;
Tlv::Bookmark tlvBookmark;
static_assert((kApplicationLayerMaxCount * (kServiceNameMaxLength + 2)) <= 250,
"Unsupported TCAT application layers configuration");
@@ -990,24 +988,18 @@ Error TcatAgent::HandleGetApplicationLayers(Message &aOutgoingMessage, bool &aRe
VerifyOrExit(mVendorInfo != nullptr, error = kErrorInvalidState);
VerifyOrExit(IsCommandClassAuthorized(kApplication), error = kErrorRejected);
SuccessOrExit(error = Tlv::StartTlv(aOutgoingMessage, kTlvResponseWithPayload, tlvBookmark));
for (uint8_t i = 0; i < kApplicationLayerMaxCount && mVendorInfo->mApplicationServiceName[i] != nullptr; i++)
{
replyLen += sizeof(tlv);
replyLen += StringLength(mVendorInfo->mApplicationServiceName[i], kServiceNameMaxLength);
count++;
}
tlv.SetType(kTlvResponseWithPayload);
tlv.SetLength(replyLen);
SuccessOrExit(error = aOutgoingMessage.Append(tlv));
for (uint8_t i = 0; i < count; i++)
{
uint16_t length = StringLength(mVendorInfo->mApplicationServiceName[i], kServiceNameMaxLength);
uint8_t type = mVendorInfo->mApplicationServiceIsTcp[i] ? kTlvServiceNameTcp : kTlvServiceNameUdp;
SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, type, mVendorInfo->mApplicationServiceName[i], length));
}
SuccessOrExit(error = Tlv::EndTlv(aOutgoingMessage, tlvBookmark));
aResponse = true;
exit:
+10 -17
View File
@@ -61,7 +61,7 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels,
{
Error error = kErrorNone;
Mle::TxMessage *message = nullptr;
Tlv tlv;
Tlv::Bookmark tlvBookmark;
Ip6::Address destination;
MeshCoP::DiscoveryRequestTlv discoveryRequest;
MeshCoP::JoinerAdvertisementTlv joinerAdvertisement;
@@ -100,32 +100,25 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels,
VerifyOrExit((message = Get<Mle>().NewMleMessage(kCommandDiscoveryRequest)) != nullptr, error = kErrorNoBufs);
message->SetPanId(aPanId);
// Prepare sub-TLV MeshCoP Discovery Request.
// Append Discovery TLV with one or two sub-TLVs.
SuccessOrExit(error = Tlv::StartTlv(*message, Tlv::kDiscovery, tlvBookmark));
discoveryRequest.Init();
discoveryRequest.SetVersion(kThreadVersion);
discoveryRequest.SetJoiner(aJoiner);
if (mAdvDataLength != 0)
{
// Prepare sub-TLV MeshCoP Joiner Advertisement.
joinerAdvertisement.Init();
joinerAdvertisement.SetOui(mOui);
joinerAdvertisement.SetAdvData(mAdvData, mAdvDataLength);
}
// Append Discovery TLV with one or two sub-TLVs.
tlv.SetType(Tlv::kDiscovery);
tlv.SetLength(
static_cast<uint8_t>(discoveryRequest.GetSize() + ((mAdvDataLength != 0) ? joinerAdvertisement.GetSize() : 0)));
SuccessOrExit(error = message->Append(tlv));
SuccessOrExit(error = discoveryRequest.AppendTo(*message));
if (mAdvDataLength != 0)
{
joinerAdvertisement.Init();
joinerAdvertisement.SetOui(mOui);
joinerAdvertisement.SetAdvData(mAdvData, mAdvDataLength);
SuccessOrExit(error = joinerAdvertisement.AppendTo(*message));
}
SuccessOrExit(error = Tlv::EndTlv(*message, tlvBookmark));
message->RegisterTxCallback(HandleDiscoveryRequestFrameTxDone, this);
destination.SetToLinkLocalAllRoutersMulticast();
+8 -16
View File
@@ -86,17 +86,14 @@ exit:
Error Initiator::AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &aInfo)
{
Error error = kErrorNone;
Tlv tlv;
Error error = kErrorNone;
Tlv::Bookmark tlvBookmark;
// The MLE Link Metrics Query TLV has two sub-TLVs:
// - Query ID sub-TLV with series ID as value.
// - Query Options sub-TLV with Type IDs as value.
tlv.SetType(Mle::Tlv::kLinkMetricsQuery);
tlv.SetLength(sizeof(Tlv) + sizeof(uint8_t) + ((aInfo.mTypeIdCount == 0) ? 0 : (sizeof(Tlv) + aInfo.mTypeIdCount)));
SuccessOrExit(error = aMessage.Append(tlv));
SuccessOrExit(error = Tlv::StartTlv(aMessage, Mle::Tlv::kLinkMetricsQuery, tlvBookmark));
SuccessOrExit(error = Tlv::Append<QueryIdSubTlv>(aMessage, aInfo.mSeriesId));
@@ -110,6 +107,8 @@ Error Initiator::AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &a
SuccessOrExit(error = aMessage.AppendBytes(aInfo.mTypeIds, aInfo.mTypeIdCount));
}
error = Tlv::EndTlv(aMessage, tlvBookmark);
exit:
return error;
}
@@ -409,13 +408,11 @@ Subject::Subject(Instance &aInstance)
Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor)
{
Error error = kErrorNone;
Tlv tlv;
Tlv::ParsedInfo tlvInfo;
uint8_t queryId;
bool hasQueryId = false;
uint16_t length;
uint16_t offset;
OffsetRange offsetRange;
Tlv::Bookmark tlvBookmark;
MetricsValues values;
values.Clear();
@@ -460,9 +457,7 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N
// Append MLE Link Metrics Report TLV and its sub-TLVs to
// `aMessage`.
offset = aMessage.GetLength();
tlv.SetType(Mle::Tlv::kLinkMetricsReport);
SuccessOrExit(error = aMessage.Append(tlv));
SuccessOrExit(error = Tlv::StartTlv(aMessage, Mle::Tlv::kLinkMetricsReport, tlvBookmark));
if (queryId == kQueryIdSingleProbe)
{
@@ -495,10 +490,7 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N
}
}
// Update the TLV length in message.
length = aMessage.GetLength() - offset - sizeof(Tlv);
tlv.SetLength(static_cast<uint8_t>(length));
aMessage.Write(offset, tlv);
error = Tlv::EndTlv(aMessage, tlvBookmark);
exit:
LogDebg("AppendReport, error:%s", ErrorToString(error));
+20 -34
View File
@@ -1413,21 +1413,15 @@ exit:
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
Error Mle::SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, LinkMetrics::Status aStatus)
{
Error error = kErrorNone;
TxMessage *message;
Tlv tlv;
ot::Tlv statusSubTlv;
Error error = kErrorNone;
TxMessage *message;
Tlv::Bookmark tlvBookmark;
VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementResponse)) != nullptr, error = kErrorNoBufs);
tlv.SetType(Tlv::kLinkMetricsManagement);
statusSubTlv.SetType(LinkMetrics::SubTlv::kStatus);
statusSubTlv.SetLength(sizeof(aStatus));
tlv.SetLength(static_cast<uint8_t>(statusSubTlv.GetSize()));
SuccessOrExit(error = message->Append(tlv));
SuccessOrExit(error = message->Append(statusSubTlv));
SuccessOrExit(error = message->Append(aStatus));
SuccessOrExit(error = Tlv::StartTlv(*message, Tlv::kLinkMetricsManagement, tlvBookmark));
SuccessOrExit(error = Tlv::Append<LinkMetrics::StatusSubTlv>(*message, aStatus));
SuccessOrExit(error = Tlv::EndTlv(*message, tlvBookmark));
SuccessOrExit(error = message->SendTo(aDestination));
@@ -1440,18 +1434,16 @@ exit:
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
Error Mle::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t *aBuf, uint8_t aLength)
{
Error error = kErrorNone;
TxMessage *message;
Tlv tlv;
Error error = kErrorNone;
TxMessage *message;
Tlv::Bookmark tlvBookmark;
VerifyOrExit((message = NewMleMessage(kCommandLinkProbe)) != nullptr, error = kErrorNoBufs);
tlv.SetType(Tlv::kLinkProbe);
tlv.SetLength(sizeof(aSeriesId) + aLength);
SuccessOrExit(error = message->Append(tlv));
SuccessOrExit(error = Tlv::StartTlv(*message, Tlv::kLinkProbe, tlvBookmark));
SuccessOrExit(error = message->Append(aSeriesId));
SuccessOrExit(error = message->AppendBytes(aBuf, aLength));
SuccessOrExit(error = Tlv::EndTlv(*message, tlvBookmark));
SuccessOrExit(error = message->SendTo(aDestination));
@@ -3654,13 +3646,11 @@ Error Mle::TxMessage::AppendVersionTlv(void) { return Tlv::Append<VersionTlv>(*t
Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode)
{
Error error = kErrorNone;
Tlv tlv;
uint8_t counter = 0;
uint16_t startOffset = GetLength();
Error error = kErrorNone;
Tlv::Bookmark tlvBookmark;
uint8_t counter = 0;
tlv.SetType(Tlv::kAddressRegistration);
SuccessOrExit(error = Append(tlv));
SuccessOrExit(error = Tlv::StartTlv(*this, Tlv::kAddressRegistration, tlvBookmark));
// Prioritize ML-EID
SuccessOrExit(error = AppendAddressRegistrationEntry(Get<Mle>().GetMeshLocalEid()));
@@ -3730,8 +3720,7 @@ exit:
if (error == kErrorNone)
{
tlv.SetLength(static_cast<uint8_t>(GetLength() - startOffset - sizeof(Tlv)));
Write(startOffset, tlv);
error = Tlv::EndTlv(*this, tlvBookmark);
}
return error;
@@ -3932,12 +3921,10 @@ Error Mle::TxMessage::AppendConnectivityTlv(void)
Error Mle::TxMessage::AppendAddressRegistrationTlv(Child &aChild)
{
Error error;
Tlv tlv;
uint16_t startOffset = GetLength();
Error error;
Tlv::Bookmark tlvBookmark;
tlv.SetType(Tlv::kAddressRegistration);
SuccessOrExit(error = Append(tlv));
SuccessOrExit(error = Tlv::StartTlv(*this, Tlv::kAddressRegistration, tlvBookmark));
// The parent must echo back all registered IPv6 addresses except
// for the ML-EID, which is excluded by `Child::GetIp6Addresses()`.
@@ -3947,8 +3934,7 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(Child &aChild)
SuccessOrExit(error = AppendAddressRegistrationEntry(address));
}
tlv.SetLength(static_cast<uint8_t>(GetLength() - startOffset - sizeof(Tlv)));
Write(startOffset, tlv);
error = Tlv::EndTlv(*this, tlvBookmark);
exit:
return error;
+3 -8
View File
@@ -2795,8 +2795,7 @@ Error Mle::SendDiscoveryResponse(const Ip6::Address &aDestination, const Discove
{
Error error = kErrorNone;
TxMessage *message;
uint16_t startOffset;
Tlv tlv;
Tlv::Bookmark tlvBookmark;
MeshCoP::DiscoveryResponseTlv discoveryResponseTlv;
VerifyOrExit((message = NewMleMessage(kCommandDiscoveryResponse)) != nullptr, error = kErrorNoBufs);
@@ -2806,10 +2805,7 @@ Error Mle::SendDiscoveryResponse(const Ip6::Address &aDestination, const Discove
message->SetRadioType(aInfo.mRadioType);
#endif
tlv.SetType(Tlv::kDiscovery);
SuccessOrExit(error = message->Append(tlv));
startOffset = message->GetLength();
SuccessOrExit(error = Tlv::StartTlv(*message, Tlv::kDiscovery, tlvBookmark));
discoveryResponseTlv.Init();
discoveryResponseTlv.SetVersion(kThreadVersion);
@@ -2854,8 +2850,7 @@ Error Mle::SendDiscoveryResponse(const Ip6::Address &aDestination, const Discove
}
#endif
tlv.SetLength(static_cast<uint8_t>(message->GetLength() - startOffset));
message->Write(startOffset - sizeof(tlv), tlv);
SuccessOrExit(error = Tlv::EndTlv(*message, tlvBookmark));
SuccessOrExit(error = message->SendTo(aDestination));
+33 -89
View File
@@ -111,37 +111,19 @@ void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::Me
Error Server::AppendIp6AddressList(Message &aMessage)
{
Error error = kErrorNone;
uint16_t count = 0;
Error error;
Tlv::Bookmark tlvBookmark;
for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
{
OT_UNUSED_VARIABLE(addr);
count++;
}
if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength)
{
Tlv tlv;
tlv.SetType(Tlv::kIp6AddressList);
tlv.SetLength(static_cast<uint8_t>(count * Ip6::Address::kSize));
SuccessOrExit(error = aMessage.Append(tlv));
}
else
{
ExtendedTlv extTlv;
extTlv.SetType(Tlv::kIp6AddressList);
extTlv.SetLength(count * Ip6::Address::kSize);
SuccessOrExit(error = aMessage.Append(extTlv));
}
SuccessOrExit(error = Tlv::StartTlv(aMessage, Tlv::kIp6AddressList, tlvBookmark));
for (const Ip6::Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
{
SuccessOrExit(error = aMessage.Append(addr.GetAddress()));
SuccessOrExit(error = Tlv::AdjustTlv(aMessage, tlvBookmark));
}
error = Tlv::EndTlv(aMessage, tlvBookmark);
exit:
return error;
}
@@ -149,36 +131,23 @@ exit:
#if OPENTHREAD_FTD
Error Server::AppendChildTable(Message &aMessage)
{
Error error = kErrorNone;
uint16_t count;
Error error = kErrorNone;
uint16_t count = 0;
Tlv::Bookmark tlvBookmark;
VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader());
count = Min(Get<ChildTable>().GetNumChildren(Child::kInStateValid), kMaxChildEntries);
if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength)
{
Tlv tlv;
tlv.SetType(Tlv::kChildTable);
tlv.SetLength(static_cast<uint8_t>(count * sizeof(ChildTableEntry)));
SuccessOrExit(error = aMessage.Append(tlv));
}
else
{
ExtendedTlv extTlv;
extTlv.SetType(Tlv::kChildTable);
extTlv.SetLength(count * sizeof(ChildTableEntry));
SuccessOrExit(error = aMessage.Append(extTlv));
}
SuccessOrExit(error = Tlv::StartTlv(aMessage, Tlv::kChildTable, tlvBookmark));
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
{
uint8_t timeout = 0;
ChildTableEntry entry;
VerifyOrExit(count--);
if (++count > kMaxChildEntries)
{
break;
}
while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
{
@@ -192,8 +161,11 @@ Error Server::AppendChildTable(Message &aMessage)
entry.SetMode(child.GetDeviceMode());
SuccessOrExit(error = aMessage.Append(entry));
SuccessOrExit(error = Tlv::AdjustTlv(aMessage, tlvBookmark));
}
error = Tlv::EndTlv(aMessage, tlvBookmark);
exit:
return error;
}
@@ -201,7 +173,7 @@ exit:
Error Server::AppendEnhancedRoute(Message &aMessage)
{
Error error = kErrorNone;
Tlv tlv;
Tlv::Bookmark tlvBookmark;
Mle::RouterIdSet routerIdSet;
EnhancedRouteTlvEntry entry;
@@ -209,10 +181,8 @@ Error Server::AppendEnhancedRoute(Message &aMessage)
Get<RouterTable>().GetRouterIdSet(routerIdSet);
tlv.SetType(Tlv::kEnhancedRoute);
tlv.SetLength(sizeof(Mle::RouterIdSet) + routerIdSet.GetNumberOfAllocatedIds() * sizeof(entry));
SuccessOrExit(error = Tlv::StartTlv(aMessage, Tlv::kEnhancedRoute, tlvBookmark));
SuccessOrExit(error = aMessage.Append(tlv));
SuccessOrExit(error = aMessage.Append(routerIdSet));
for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++)
@@ -234,6 +204,8 @@ Error Server::AppendEnhancedRoute(Message &aMessage)
SuccessOrExit(error = aMessage.Append(entry));
}
error = Tlv::EndTlv(aMessage, tlvBookmark);
exit:
return error;
}
@@ -332,32 +304,21 @@ Error Server::AppendMacCounters(Message &aMessage)
Error Server::AppendBorderRouterIfAddrs(Message &aMessage)
{
Error error;
Tlv tlv;
uint16_t offset;
uint16_t length = 0;
Tlv::Bookmark tlvBookmark;
BorderRouter::PrefixTableIterator iterator;
BorderRouter::IfAddrEntry ifAddr;
tlv.SetType(Tlv::kBrIfAddrs);
offset = aMessage.GetLength();
SuccessOrExit(error = aMessage.Append(tlv));
SuccessOrExit(error = Tlv::StartTlv(aMessage, Tlv::kBrIfAddrs, tlvBookmark));
Get<BorderRouter::RxRaTracker>().InitIterator(iterator);
while (Get<BorderRouter::RxRaTracker>().GetNextIfAddrEntry(iterator, ifAddr) == kErrorNone)
{
if (length + sizeof(Ip6::Address) > Tlv::kBaseTlvMaxLength)
{
break;
}
SuccessOrExit(error = aMessage.Append(ifAddr.mAddress));
length += sizeof(Ip6::Address);
SuccessOrExit(error = Tlv::AdjustTlv(aMessage, tlvBookmark));
}
tlv.SetLength(ClampToUint8(length));
aMessage.Write(offset, tlv);
error = Tlv::EndTlv(aMessage, tlvBookmark);
exit:
return error;
@@ -1011,38 +972,18 @@ exit:
Error Server::AppendChildIp6AddressListTlv(Message &aAnswer, const Child &aChild)
{
Error error = kErrorNone;
uint16_t numIp6Addr = aChild.GetIp6Addresses().GetLength();
Error error = kErrorNone;
Tlv::Bookmark tlvBookmark;
ChildIp6AddressListTlvValue tlvValue;
Ip6::Address mlEid;
if (aChild.GetMeshLocalIp6Address(mlEid) == kErrorNone)
{
numIp6Addr++;
}
else
if (aChild.GetMeshLocalIp6Address(mlEid) != kErrorNone)
{
mlEid.Clear();
VerifyOrExit(!aChild.GetIp6Addresses().IsEmpty());
}
VerifyOrExit(numIp6Addr > 0);
if ((numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)) <= Tlv::kBaseTlvMaxLength)
{
Tlv tlv;
tlv.SetType(Tlv::kChildIp6AddressList);
tlv.SetLength(static_cast<uint8_t>(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue)));
SuccessOrExit(error = aAnswer.Append(tlv));
}
else
{
ExtendedTlv extTlv;
extTlv.SetType(Tlv::kChildIp6AddressList);
extTlv.SetLength(numIp6Addr * sizeof(Ip6::Address) + sizeof(ChildIp6AddressListTlvValue));
SuccessOrExit(error = aAnswer.Append(extTlv));
}
SuccessOrExit(error = Tlv::StartTlv(aAnswer, Tlv::kChildIp6AddressList, tlvBookmark));
tlvValue.SetRloc16(aChild.GetRloc16());
@@ -1056,8 +997,11 @@ Error Server::AppendChildIp6AddressListTlv(Message &aAnswer, const Child &aChild
for (const Ip6::Address &address : aChild.GetIp6Addresses())
{
SuccessOrExit(error = aAnswer.Append(address));
SuccessOrExit(error = Tlv::AdjustTlv(aAnswer, tlvBookmark));
}
error = Tlv::EndTlv(aAnswer, tlvBookmark);
exit:
return error;
}
+112 -8
View File
@@ -35,19 +35,25 @@
#include "instance/instance.hpp"
#include "test_util.h"
#include "test_util.hpp"
namespace ot {
void TestTlv(void)
{
Instance *instance = testInitInstance();
Message *message;
Tlv tlv;
ExtendedTlv extTlv;
uint16_t offset;
OffsetRange offsetRange;
uint16_t length;
uint8_t buffer[4];
static constexpr uint16_t kMaxBufferSize = 300;
Instance *instance = testInitInstance();
Message *message;
Tlv tlv;
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);
@@ -179,6 +185,104 @@ void TestTlv(void)
VerifyOrQuit(Tlv::FindTlvValueOffsetRange(*message, /* aType */ 7, offsetRange) != kErrorNone);
VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == 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(Tlv::ReadTlvValue(*message, offset, 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);