diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 2fe410ba1..9f1717ed2 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -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(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; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 89014d5a0..505b9d742 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -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 static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue); template static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue); template static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue); diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 4b522e73f..c9db9791b 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -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: diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index 63af6b152..5fe360f09 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -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().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(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(); diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index f7c778aa9..f362939ac 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -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(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(length)); - aMessage.Write(offset, tlv); + error = Tlv::EndTlv(aMessage, tlvBookmark); exit: LogDebg("AppendReport, error:%s", ErrorToString(error)); diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index ab0f9ef6f..1a524a10e 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -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(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(*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(*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().GetMeshLocalEid())); @@ -3730,8 +3720,7 @@ exit: if (error == kErrorNone) { - tlv.SetLength(static_cast(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(GetLength() - startOffset - sizeof(Tlv))); - Write(startOffset, tlv); + error = Tlv::EndTlv(*this, tlvBookmark); exit: return error; diff --git a/src/core/thread/mle_ftd.cpp b/src/core/thread/mle_ftd.cpp index 49be27f46..db6d6a09d 100644 --- a/src/core/thread/mle_ftd.cpp +++ b/src/core/thread/mle_ftd.cpp @@ -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(message->GetLength() - startOffset)); - message->Write(startOffset - sizeof(tlv), tlv); + SuccessOrExit(error = Tlv::EndTlv(*message, tlvBookmark)); SuccessOrExit(error = message->SendTo(aDestination)); diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index ffc27c4a8..458854323 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -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().GetUnicastAddresses()) - { - OT_UNUSED_VARIABLE(addr); - count++; - } - - if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength) - { - Tlv tlv; - - tlv.SetType(Tlv::kIp6AddressList); - tlv.SetLength(static_cast(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().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().IsRouterOrLeader()); - count = Min(Get().GetNumChildren(Child::kInStateValid), kMaxChildEntries); - - if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength) - { - Tlv tlv; - - tlv.SetType(Tlv::kChildTable); - tlv.SetLength(static_cast(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().Iterate(Child::kInStateValid)) { uint8_t timeout = 0; ChildTableEntry entry; - VerifyOrExit(count--); + if (++count > kMaxChildEntries) + { + break; + } while (static_cast(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().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().InitIterator(iterator); while (Get().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(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; } diff --git a/tests/unit/test_tlv.cpp b/tests/unit/test_tlv.cpp index 165782642..e93886586 100644 --- a/tests/unit/test_tlv.cpp +++ b/tests/unit/test_tlv.cpp @@ -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(0xab)); + SuccessOrQuit(message->Append(0xcd)); + SuccessOrQuit(message->Append(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(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(buffer[index])); + + prevLength = message->GetLength(); + SuccessOrQuit(Tlv::AdjustTlv(*message, bookmark)); + VerifyOrQuit(prevLength == message->GetLength()); + } + + SuccessOrQuit(message->Append(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(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);