mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[network-diag] introduce AnswerBuilder to manage answer messages (#12887)
This commit introduces `AnswerBuilder` class to track and manage Network Diagnostic answer messages. This class is used when the response to a query requires multiple CoAP answer messages. It automatically manages the inclusion of the Query ID and the Answer TLVs(providing message indexing and "more-to-follow" flags) in each allocated answer message, while maintaining all answer messages in a queue. The `NetworkDiagnostic::Server` is updated to use the `AnswerBuilder`, simplifying the logic for preparing and sending answers. The `AnswerBuilder` class is added in a new header file `network_diagnostic_types.hpp` to allow for its reuse by other modules in the future.
This commit is contained in:
committed by
GitHub
parent
254043deec
commit
7eb59f71da
@@ -755,6 +755,8 @@ openthread_core_files = [
|
||||
"thread/network_diagnostic.hpp",
|
||||
"thread/network_diagnostic_tlvs.cpp",
|
||||
"thread/network_diagnostic_tlvs.hpp",
|
||||
"thread/network_diagnostic_types.cpp",
|
||||
"thread/network_diagnostic_types.hpp",
|
||||
"thread/panid_query_server.cpp",
|
||||
"thread/panid_query_server.hpp",
|
||||
"thread/peer.cpp",
|
||||
|
||||
@@ -268,6 +268,7 @@ set(COMMON_SOURCES
|
||||
thread/network_data_types.cpp
|
||||
thread/network_diagnostic.cpp
|
||||
thread/network_diagnostic_tlvs.cpp
|
||||
thread/network_diagnostic_types.cpp
|
||||
thread/panid_query_server.cpp
|
||||
thread/peer.cpp
|
||||
thread/peer_table.cpp
|
||||
|
||||
@@ -980,6 +980,30 @@ void MessageQueue::DequeueAndFreeAll(void)
|
||||
}
|
||||
}
|
||||
|
||||
void MessageQueue::EnqueueAllFrom(MessageQueue &aOtherQueue)
|
||||
{
|
||||
VerifyOrExit(&aOtherQueue != this);
|
||||
|
||||
VerifyOrExit(aOtherQueue.GetHead() != nullptr);
|
||||
|
||||
if (GetHead() == nullptr)
|
||||
{
|
||||
SetHead(aOtherQueue.GetHead());
|
||||
}
|
||||
else
|
||||
{
|
||||
GetTail()->Next() = aOtherQueue.GetHead();
|
||||
aOtherQueue.GetHead()->Prev() = GetTail();
|
||||
}
|
||||
|
||||
SetTail(aOtherQueue.GetTail());
|
||||
|
||||
aOtherQueue.Clear();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Message::Iterator MessageQueue::begin(void) { return Message::Iterator(GetHead()); }
|
||||
|
||||
Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIterator(GetHead()); }
|
||||
|
||||
@@ -1731,6 +1731,15 @@ public:
|
||||
*/
|
||||
void DequeueAndFreeAll(void);
|
||||
|
||||
/**
|
||||
* Enqueues all messages from another message queue at the end of this queue.
|
||||
*
|
||||
* Upon return, @p aOtherQueue will be empty.
|
||||
*
|
||||
* @param[in,out] aOtherQueue The other message queue to enqueue from.
|
||||
*/
|
||||
void EnqueueAllFrom(MessageQueue &aOtherQueue);
|
||||
|
||||
/**
|
||||
* Gets the information about number of messages and buffers in the queue.
|
||||
*
|
||||
|
||||
@@ -539,34 +539,6 @@ exit:
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
Error Server::AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
// Allocate an `Answer` message, adds it in `mAnswerQueue`,
|
||||
// update the `aInfo.mFirstAnswer` if it is the first allocated
|
||||
// messages, and appends `QueryIdTlv` to the message (if needed).
|
||||
|
||||
Error error = kErrorNone;
|
||||
|
||||
aAnswer = Get<Tmf::Agent>().AllocateAndInitConfirmablePostMessage(kUriDiagnosticGetAnswer);
|
||||
VerifyOrExit(aAnswer != nullptr, error = kErrorNoBufs);
|
||||
IgnoreError(aAnswer->SetPriority(aInfo.mPriority));
|
||||
|
||||
mAnswerQueue.Enqueue(*aAnswer);
|
||||
|
||||
if (aInfo.mFirstAnswer == nullptr)
|
||||
{
|
||||
aInfo.mFirstAnswer = aAnswer;
|
||||
}
|
||||
|
||||
if (aInfo.mHasQueryId)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*aAnswer, aInfo.mQueryId));
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool Server::IsLastAnswer(const Coap::Message &aAnswer) const
|
||||
{
|
||||
// Indicates whether `aAnswer` is the last one associated with
|
||||
@@ -601,23 +573,29 @@ void Server::FreeAllRelatedAnswers(Coap::Message &aFirstAnswer)
|
||||
}
|
||||
}
|
||||
|
||||
void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest)
|
||||
void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Coap::Message &aRequest)
|
||||
{
|
||||
AnswerBuilder answerBuilder(GetInstance());
|
||||
Coap::Message *firstAnswer;
|
||||
|
||||
SuccessOrExit(PrepareAnswers(aRequest, answerBuilder));
|
||||
|
||||
firstAnswer = answerBuilder.GetAnswers().GetHead();
|
||||
mAnswerQueue.EnqueueAllFrom(answerBuilder.GetAnswers());
|
||||
|
||||
SendNextAnswer(*firstAnswer, aDestination);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Error Server::PrepareAnswers(const Coap::Message &aRequest, AnswerBuilder &aAnswerBuilder)
|
||||
{
|
||||
Coap::Message *answer;
|
||||
Error error;
|
||||
AnswerInfo info;
|
||||
uint8_t tlvType;
|
||||
TlvTypeListIterator iterator;
|
||||
AnswerTlvValue answerTlvValue;
|
||||
|
||||
if (Tlv::Find<QueryIdTlv>(aRequest, info.mQueryId) == kErrorNone)
|
||||
{
|
||||
info.mHasQueryId = true;
|
||||
}
|
||||
|
||||
info.mPriority = aRequest.GetPriority();
|
||||
|
||||
SuccessOrExit(error = AllocateAnswer(answer, info));
|
||||
SuccessOrExit(error = aAnswerBuilder.Start(aRequest));
|
||||
|
||||
SuccessOrExit(error = iterator.InitForTypeListTlv(aRequest));
|
||||
|
||||
@@ -626,51 +604,23 @@ void Server::PrepareAndSendAnswers(const Ip6::Address &aDestination, const Messa
|
||||
switch (tlvType)
|
||||
{
|
||||
case ChildTlv::kType:
|
||||
SuccessOrExit(error = AppendChildTableAsChildTlvs(answer, info));
|
||||
SuccessOrExit(error = AppendChildTableAsChildTlvs(aAnswerBuilder));
|
||||
break;
|
||||
case ChildIp6AddressListTlv::kType:
|
||||
SuccessOrExit(error = AppendChildTableIp6AddressList(answer, info));
|
||||
SuccessOrExit(error = AppendChildTableIp6AddressList(aAnswerBuilder));
|
||||
break;
|
||||
case RouterNeighborTlv::kType:
|
||||
SuccessOrExit(error = AppendRouterNeighborTlvs(answer, info));
|
||||
SuccessOrExit(error = AppendRouterNeighborTlvs(aAnswerBuilder));
|
||||
break;
|
||||
default:
|
||||
SuccessOrExit(error = AppendDiagTlv(tlvType, *answer));
|
||||
SuccessOrExit(error = AppendDiagTlv(tlvType, aAnswerBuilder.GetAnswer()));
|
||||
break;
|
||||
}
|
||||
|
||||
SuccessOrExit(error = CheckAnswerLength(answer, info));
|
||||
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
|
||||
}
|
||||
|
||||
answerTlvValue.Init(info.mAnswerIndex, AnswerTlvValue::kIsLast);
|
||||
SuccessOrExit(error = Tlv::Append<AnswerTlv>(*answer, answerTlvValue));
|
||||
|
||||
SendNextAnswer(*info.mFirstAnswer, aDestination);
|
||||
|
||||
exit:
|
||||
if ((error != kErrorNone) && (info.mFirstAnswer != nullptr))
|
||||
{
|
||||
FreeAllRelatedAnswers(*info.mFirstAnswer);
|
||||
}
|
||||
}
|
||||
|
||||
Error Server::CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
{
|
||||
// This method checks the length of the `aAnswer` message and if it
|
||||
// is above the threshold, it enqueues the message for transmission
|
||||
// after appending an Answer TLV with the current index to the
|
||||
// message. In this case, it will also allocate a new answer
|
||||
// message.
|
||||
|
||||
Error error = kErrorNone;
|
||||
AnswerTlvValue answerTlvValue;
|
||||
|
||||
VerifyOrExit(aAnswer->GetLength() >= kAnswerMessageLengthThreshold);
|
||||
|
||||
answerTlvValue.Init(aInfo.mAnswerIndex++, AnswerTlvValue::kMoreToFollow);
|
||||
SuccessOrExit(error = Tlv::Append<AnswerTlv>(*aAnswer, answerTlvValue));
|
||||
|
||||
error = AllocateAnswer(aAnswer, aInfo);
|
||||
SuccessOrExit(error = aAnswerBuilder.Finish());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -734,7 +684,7 @@ exit:
|
||||
}
|
||||
}
|
||||
|
||||
Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
Error Server::AppendChildTableAsChildTlvs(AnswerBuilder &aAnswerBuilder)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
ChildTlvValue childTlvValue;
|
||||
@@ -743,17 +693,17 @@ Error Server::AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &a
|
||||
{
|
||||
childTlvValue.InitFrom(child);
|
||||
|
||||
SuccessOrExit(error = Tlv::Append<ChildTlv>(*aAnswer, childTlvValue));
|
||||
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
|
||||
SuccessOrExit(error = Tlv::Append<ChildTlv>(aAnswerBuilder.GetAnswer(), childTlvValue));
|
||||
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
|
||||
}
|
||||
|
||||
error = Tlv::AppendEmpty<ChildTlv>(*aAnswer);
|
||||
error = Tlv::AppendEmpty<ChildTlv>(aAnswerBuilder.GetAnswer());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
Error Server::AppendRouterNeighborTlvs(AnswerBuilder &aAnswerBuilder)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
RouterNeighborTlvValue neighborTlvValue;
|
||||
@@ -767,27 +717,27 @@ Error Server::AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInf
|
||||
|
||||
neighborTlvValue.InitFrom(router);
|
||||
|
||||
SuccessOrExit(error = Tlv::Append<RouterNeighborTlv>(*aAnswer, neighborTlvValue));
|
||||
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
|
||||
SuccessOrExit(error = Tlv::Append<RouterNeighborTlv>(aAnswerBuilder.GetAnswer(), neighborTlvValue));
|
||||
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
|
||||
}
|
||||
|
||||
error = Tlv::AppendEmpty<RouterNeighborTlv>(*aAnswer);
|
||||
error = Tlv::AppendEmpty<RouterNeighborTlv>(aAnswerBuilder.GetAnswer());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo)
|
||||
Error Server::AppendChildTableIp6AddressList(AnswerBuilder &aAnswerBuilder)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
for (const Child &child : Get<ChildTable>().Iterate(Child::kInStateValid))
|
||||
{
|
||||
SuccessOrExit(error = AppendChildIp6AddressListTlv(*aAnswer, child));
|
||||
SuccessOrExit(error = CheckAnswerLength(aAnswer, aInfo));
|
||||
SuccessOrExit(error = AppendChildIp6AddressListTlv(aAnswerBuilder.GetAnswer(), child));
|
||||
SuccessOrExit(error = aAnswerBuilder.CheckAnswerLength());
|
||||
}
|
||||
|
||||
error = Tlv::AppendEmpty<ChildIp6AddressListTlv>(*aAnswer);
|
||||
error = Tlv::AppendEmpty<ChildIp6AddressListTlv>(aAnswerBuilder.GetAnswer());
|
||||
|
||||
exit:
|
||||
return error;
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "common/numeric_limits.hpp"
|
||||
#include "net/udp6.hpp"
|
||||
#include "thread/network_diagnostic_tlvs.hpp"
|
||||
#include "thread/network_diagnostic_types.hpp"
|
||||
#include "thread/tmf.hpp"
|
||||
#include "thread/uri_paths.hpp"
|
||||
|
||||
@@ -119,8 +120,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint16_t kMaxChildEntries = 398;
|
||||
static constexpr uint16_t kAnswerMessageLengthThreshold = 800;
|
||||
static constexpr uint16_t kMaxChildEntries = 398;
|
||||
|
||||
class TlvTypeListIterator
|
||||
{
|
||||
@@ -139,25 +139,6 @@ private:
|
||||
BitSet<NumericLimits<uint8_t>::kMax + 1> mProcessedTlvs;
|
||||
};
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
struct AnswerInfo
|
||||
{
|
||||
AnswerInfo(void)
|
||||
: mAnswerIndex(0)
|
||||
, mQueryId(0)
|
||||
, mHasQueryId(false)
|
||||
, mFirstAnswer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t mAnswerIndex;
|
||||
uint16_t mQueryId;
|
||||
bool mHasQueryId;
|
||||
Message::Priority mPriority;
|
||||
Coap::Message *mFirstAnswer;
|
||||
};
|
||||
#endif
|
||||
|
||||
Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage);
|
||||
Error AppendIp6AddressList(Message &aMessage);
|
||||
Error AppendRequestedTlvs(const Message &aRequest, Message &aResponse);
|
||||
@@ -169,16 +150,15 @@ private:
|
||||
#if OPENTHREAD_MTD
|
||||
void SendAnswer(const Ip6::Address &aDestination, const Message &aRequest);
|
||||
#elif OPENTHREAD_FTD
|
||||
Error AllocateAnswer(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error CheckAnswerLength(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
bool IsLastAnswer(const Coap::Message &aAnswer) const;
|
||||
void FreeAllRelatedAnswers(Coap::Message &aFirstAnswer);
|
||||
void PrepareAndSendAnswers(const Ip6::Address &aDestination, const Message &aRequest);
|
||||
void PrepareAndSendAnswers(const Ip6::Address &aDestination, const Coap::Message &aRequest);
|
||||
Error PrepareAnswers(const Coap::Message &aRequest, AnswerBuilder &aAnswerBuilder);
|
||||
void SendNextAnswer(Coap::Message &aAnswer, const Ip6::Address &aDestination);
|
||||
Error AppendChildTable(Message &aMessage);
|
||||
Error AppendChildTableAsChildTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error AppendRouterNeighborTlvs(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo &aInfo);
|
||||
Error AppendChildTableAsChildTlvs(AnswerBuilder &aAnswerBuilder);
|
||||
Error AppendRouterNeighborTlvs(AnswerBuilder &aAnswerBuilder);
|
||||
Error AppendChildTableIp6AddressList(AnswerBuilder &aAnswerBuilder);
|
||||
Error AppendChildIp6AddressListTlv(Message &aAnswer, const Child &aChild);
|
||||
Error AppendEnhancedRoute(Message &aMessage);
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file implements methods for Network Diagnostics helper types and classes.
|
||||
*/
|
||||
|
||||
#include "network_diagnostic_types.hpp"
|
||||
|
||||
#include "instance/instance.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace NetworkDiagnostic {
|
||||
|
||||
AnswerBuilder::AnswerBuilder(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mAnswer(nullptr)
|
||||
, mAnswerIndex(0)
|
||||
, mQueryId(0)
|
||||
, mHasQueryId(false)
|
||||
, mPriority(Message::kPriorityNormal)
|
||||
{
|
||||
}
|
||||
|
||||
AnswerBuilder::~AnswerBuilder(void) { mAnswers.DequeueAndFreeAll(); }
|
||||
|
||||
Error AnswerBuilder::Start(const Coap::Message &aRequest)
|
||||
{
|
||||
mAnswerIndex = 0;
|
||||
mHasQueryId = (Tlv::Find<QueryIdTlv>(aRequest, mQueryId) == kErrorNone);
|
||||
mPriority = aRequest.GetPriority();
|
||||
|
||||
return Allocate();
|
||||
}
|
||||
|
||||
Error AnswerBuilder::Finish(void) { return AppendAnswerTlv(AnswerTlvValue::kIsLast); }
|
||||
|
||||
Error AnswerBuilder::Allocate(void)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
mAnswer = Get<Tmf::Agent>().AllocateAndInitConfirmablePostMessage(kUriDiagnosticGetAnswer);
|
||||
VerifyOrExit(mAnswer != nullptr, error = kErrorNoBufs);
|
||||
|
||||
IgnoreError(mAnswer->SetPriority(mPriority));
|
||||
|
||||
mAnswers.Enqueue(*mAnswer);
|
||||
|
||||
if (mHasQueryId)
|
||||
{
|
||||
SuccessOrExit(error = Tlv::Append<QueryIdTlv>(*mAnswer, mQueryId));
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error AnswerBuilder::AppendAnswerTlv(AnswerTlvValue::IsLastFlag aFlag)
|
||||
{
|
||||
AnswerTlvValue value;
|
||||
|
||||
value.Init(mAnswerIndex++, aFlag);
|
||||
|
||||
return Tlv::Append<AnswerTlv>(*mAnswer, value);
|
||||
}
|
||||
|
||||
Error AnswerBuilder::CheckAnswerLength(void)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(mAnswer->GetLength() >= kAnswerMessageLengthThreshold);
|
||||
|
||||
SuccessOrExit(error = AppendAnswerTlv(AnswerTlvValue::kMoreToFollow));
|
||||
|
||||
error = Allocate();
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
} // namespace NetworkDiagnostic
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2026, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file includes definitions for Network Diagnostics helper types and classes.
|
||||
*/
|
||||
|
||||
#ifndef OT_CORE_THREAD_NETWORK_DIAGNOSTIC_TYPES_HPP_
|
||||
#define OT_CORE_THREAD_NETWORK_DIAGNOSTIC_TYPES_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include "network_diagnostic_tlvs.hpp"
|
||||
#include "coap/coap_message.hpp"
|
||||
#include "common/error.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/time.hpp"
|
||||
#include "thread/uri_paths.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace NetworkDiagnostic {
|
||||
|
||||
/**
|
||||
* Represents an object for tracking and managing Network Diagnostic answer messages.
|
||||
*
|
||||
* This class is used when the response to a Network Diagnostic query requires multiple CoAP answer messages . It
|
||||
* manages the inclusion of the Query ID and the Answer TLV (providing message indexing and "more-to-follow" flags)
|
||||
* in each allocated answer message, while maintaining all answer messages in a queue.
|
||||
*/
|
||||
class AnswerBuilder : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the `AnswerBuilder`.
|
||||
*
|
||||
* @param[in] aInstance The OpenThread instance.
|
||||
*/
|
||||
explicit AnswerBuilder(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* Destructor for `AnswerBuilder`.
|
||||
*
|
||||
* Any remaining messages in the answers queue are freed.
|
||||
*/
|
||||
~AnswerBuilder(void);
|
||||
|
||||
/**
|
||||
* Starts the building of answer messages based on a given Network Diagnostic request.
|
||||
*
|
||||
* This method searches for a Query ID TLV within @p aRequest. If present, the Query ID is captured and
|
||||
* automatically included in all allocated answer messages. It also captures the priority from @p aRequest and
|
||||
* uses it for all answer messages.
|
||||
*
|
||||
* @param[in] aRequest The Network Diagnostic request message.
|
||||
*
|
||||
* @retval kErrorNone Successfully started.
|
||||
* @retval kErrorNoBufs Insufficient message buffers to allocate the first answer.
|
||||
*/
|
||||
Error Start(const Coap::Message &aRequest);
|
||||
|
||||
/**
|
||||
* Gets the current answer message.
|
||||
*
|
||||
* @returns The current answer message.
|
||||
*/
|
||||
Coap::Message &GetAnswer(void) { return *mAnswer; }
|
||||
|
||||
/**
|
||||
* Checks the current answer message length and allocates a new one if it exceeds the threshold.
|
||||
*
|
||||
* If the current answer message length is above the threshold, this method appends an Answer TLV with the
|
||||
* "more to follow" flag set, and then allocates a new answer message, adding it to the answer queue.
|
||||
*
|
||||
* @retval kErrorNone Successfully checked and handled (possibly allocating a new message).
|
||||
* @retval kErrorNoBufs Insufficient message buffers to append Answer TLV or allocate a new message.
|
||||
*/
|
||||
Error CheckAnswerLength(void);
|
||||
|
||||
/**
|
||||
* Finishes the answer generation.
|
||||
*
|
||||
* This method appends an Answer TLV to the current answer message with the "is last" flag set.
|
||||
*
|
||||
* @retval kErrorNone Successfully finished.
|
||||
* @retval kErrorNoBufs Insufficient message buffers to append the Answer TLV.
|
||||
*/
|
||||
Error Finish(void);
|
||||
|
||||
/**
|
||||
* Gets the queue containing all answer messages.
|
||||
*
|
||||
* @returns The message queue containing all answer messages.
|
||||
*/
|
||||
Coap::MessageQueue &GetAnswers(void) { return mAnswers; }
|
||||
|
||||
private:
|
||||
static constexpr uint16_t kAnswerMessageLengthThreshold = 800;
|
||||
|
||||
Error Allocate(void);
|
||||
Error AppendAnswerTlv(AnswerTlvValue::IsLastFlag aFlag);
|
||||
|
||||
Coap::Message *mAnswer;
|
||||
Coap::MessageQueue mAnswers;
|
||||
uint16_t mAnswerIndex;
|
||||
uint16_t mQueryId;
|
||||
bool mHasQueryId;
|
||||
Message::Priority mPriority;
|
||||
};
|
||||
|
||||
} // namespace NetworkDiagnostic
|
||||
} // namespace ot
|
||||
|
||||
#endif // OT_CORE_THREAD_NETWORK_DIAGNOSTIC_TYPES_HPP_
|
||||
@@ -286,6 +286,59 @@ void TestMessageQueue(void)
|
||||
testFreeInstance(sInstance);
|
||||
}
|
||||
|
||||
void TestMessageQueueEnqueueAllFrom(void)
|
||||
{
|
||||
// Validates the behavior of `MessageQueue::EnqueueAllFrom()` method.
|
||||
|
||||
MessageQueue messageQueue1;
|
||||
MessageQueue messageQueue2;
|
||||
Message *messages[kNumTestMessages];
|
||||
|
||||
sInstance = testInitInstance();
|
||||
VerifyOrQuit(sInstance != nullptr);
|
||||
|
||||
sMessagePool = &sInstance->Get<MessagePool>();
|
||||
|
||||
for (Message *&msg : messages)
|
||||
{
|
||||
msg = sMessagePool->Allocate(Message::kTypeIp6);
|
||||
VerifyOrQuit(msg != nullptr, "Message::Allocate() failed");
|
||||
}
|
||||
|
||||
// Case 1: Both queues are empty
|
||||
messageQueue1.EnqueueAllFrom(messageQueue2);
|
||||
VerifyMessageQueueContent(messageQueue1, 0);
|
||||
VerifyMessageQueueContent(messageQueue2, 0);
|
||||
|
||||
// Case 2: Target queue (Q1) is empty, input queue (Q2) is non-empty
|
||||
messageQueue2.Enqueue(*messages[0]);
|
||||
messageQueue2.Enqueue(*messages[1]);
|
||||
VerifyMessageQueueContent(messageQueue2, 2, messages[0], messages[1]);
|
||||
messageQueue1.EnqueueAllFrom(messageQueue2);
|
||||
VerifyMessageQueueContent(messageQueue1, 2, messages[0], messages[1]);
|
||||
VerifyMessageQueueContent(messageQueue2, 0);
|
||||
|
||||
// Case 3: Target queue (Q1) is non-empty, input queue (Q2) is empty
|
||||
messageQueue1.EnqueueAllFrom(messageQueue2);
|
||||
VerifyMessageQueueContent(messageQueue1, 2, messages[0], messages[1]);
|
||||
VerifyMessageQueueContent(messageQueue2, 0);
|
||||
|
||||
// Case 4: Both queues are non-empty
|
||||
messageQueue2.Enqueue(*messages[2]);
|
||||
VerifyMessageQueueContent(messageQueue2, 1, messages[2]);
|
||||
messageQueue1.EnqueueAllFrom(messageQueue2);
|
||||
VerifyMessageQueueContent(messageQueue1, 3, messages[0], messages[1], messages[2]);
|
||||
VerifyMessageQueueContent(messageQueue2, 0);
|
||||
|
||||
// Case 5: Using the same queue as both the target and input queues
|
||||
messageQueue1.EnqueueAllFrom(messageQueue1);
|
||||
VerifyMessageQueueContent(messageQueue1, 3, messages[0], messages[1], messages[2]);
|
||||
messageQueue2.EnqueueAllFrom(messageQueue2);
|
||||
VerifyMessageQueueContent(messageQueue2, 0);
|
||||
|
||||
testFreeInstance(sInstance);
|
||||
}
|
||||
|
||||
// This function verifies the content of the message queue to match the passed in messages
|
||||
void VerifyMessageQueueContentUsingOtApi(otMessageQueue *aQueue, int aExpectedLength, ...)
|
||||
{
|
||||
@@ -386,6 +439,7 @@ void TestMessageQueueOtApis(void)
|
||||
int main(void)
|
||||
{
|
||||
ot::TestMessageQueue();
|
||||
ot::TestMessageQueueEnqueueAllFrom();
|
||||
ot::TestMessageQueueOtApis();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user