From d81c6fab98df8fbeb0f3611ce5ec4c131c84cbce Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Sat, 6 Jan 2024 14:10:27 +0800 Subject: [PATCH] [telemetry] add API for TREL telemetry (#9710) Create OT API to support trel telemetry which is supported through platform API and also add cli support to get/reset trel counters. Metrics we are adding are: - trel_frames_tx - trel_bytes_tx - trel_frames_rx - trel_bytes_rx - trel_frames_tx_failed - num_trel_peers Metrics already supported through API: - trel_enabled --- examples/platforms/simulation/trel.c | 23 +++++++-- include/openthread/instance.h | 2 +- include/openthread/platform/trel.h | 29 +++++++++++ include/openthread/trel.h | 35 ++++++++++++++ src/cli/cli.cpp | 48 +++++++++++++++++++ src/cli/cli.hpp | 3 ++ src/core/api/trel_api.cpp | 12 +++++ src/core/radio/trel_interface.cpp | 4 ++ src/core/radio/trel_interface.hpp | 28 +++++++++++ src/posix/platform/trel.cpp | 42 +++++++++++++--- .../border_router/test_trel_connectivity.py | 17 +++++++ tests/scripts/thread-cert/node.py | 24 ++++++++++ tests/unit/test_platform.cpp | 4 ++ tests/unit/test_platform.h | 1 + 14 files changed, 261 insertions(+), 11 deletions(-) diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c index 3413c9351..cfc34ac9b 100644 --- a/examples/platforms/simulation/trel.c +++ b/examples/platforms/simulation/trel.c @@ -73,10 +73,11 @@ static uint16_t sPortOffset = 0; static bool sEnabled = false; static uint16_t sUdpPort; -static bool sServiceRegistered = false; -static uint16_t sServicePort; -static uint8_t sServiceTxtLength; -static char sServiceTxtData[TREL_MAX_SERVICE_TXT_DATA_LEN]; +static bool sServiceRegistered = false; +static uint16_t sServicePort; +static uint8_t sServiceTxtLength; +static char sServiceTxtData[TREL_MAX_SERVICE_TXT_DATA_LEN]; +static otPlatTrelCounters sCounters; #if DEBUG_LOG static void dumpBuffer(const void *aBuffer, uint16_t aLength) @@ -388,6 +389,8 @@ void otPlatTrelSend(otInstance *aInstance, #if DEBUG_LOG fprintf(stderr, "\r\n[trel-sim] otPlatTrelSend(len:%u, port:%u)\r\n", aUdpPayloadLen, aDestSockAddr->mPort); #endif + ++sCounters.mTxPackets; + sCounters.mTxBytes += aUdpPayloadLen; } //--------------------------------------------------------------------------------------------------------------------- @@ -473,6 +476,18 @@ void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const } } +const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return &sCounters; +} + +void otPlatTrelResetCounters(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + memset(&sCounters, 0, sizeof(sCounters)); +} + //--------------------------------------------------------------------------------------------------------------------- // This is added for RCP build to be built ok diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 6394f343e..05c7ce4f0 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (387) +#define OPENTHREAD_API_VERSION (388) /** * @addtogroup api-instance diff --git a/include/openthread/platform/trel.h b/include/openthread/platform/trel.h index b372fded5..f9e068f77 100644 --- a/include/openthread/platform/trel.h +++ b/include/openthread/platform/trel.h @@ -197,6 +197,35 @@ void otPlatTrelSend(otInstance *aInstance, */ extern void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength); +/** + * Represents a group of TREL related counters in the platform layer. + * + */ +typedef struct otPlatTrelCounters +{ + uint64_t mTxPackets; ///< Number of packets successfully transmitted through TREL. + uint64_t mTxBytes; ///< Sum of size of packets successfully transmitted through TREL. + uint64_t mTxFailure; ///< Number of packet transmission failures through TREL. + uint64_t mRxPackets; ///< Number of packets received through TREL. + uint64_t mRxBytes; ///< Sum of size of packets received through TREL. +} otPlatTrelCounters; + +/** + * Gets the pointer to the TREL counters in the platform layer. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance); + +/** + * Resets the TREL counters in the platform layer. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void otPlatTrelResetCounters(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/trel.h b/include/openthread/trel.h index 2358ea26a..e9a0bf00a 100644 --- a/include/openthread/trel.h +++ b/include/openthread/trel.h @@ -39,6 +39,7 @@ #include #include #include +#include "openthread/platform/trel.h" #ifdef __cplusplus extern "C" { @@ -123,6 +124,16 @@ void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator */ const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator); +/** + * Returns the number of TREL peers. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The number of TREL peers. + * + */ +uint16_t otTrelGetNumberOfPeers(otInstance *aInstance); + /** * Sets the filter mode (enables/disables filtering). * @@ -149,6 +160,30 @@ void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable); */ bool otTrelIsFilterEnabled(otInstance *aInstance); +/** + * Represents a group of TREL related counters. + * + */ +typedef otPlatTrelCounters otTrelCounters; + +/** + * Gets the TREL counters. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the TREL counters. + * + */ +const otTrelCounters *otTrelGetCounters(otInstance *aInstance); + +/** + * Resets the TREL counters. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otTrelResetCounters(otInstance *aInstance); + /** * @} * diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index ba34e59b4..f288740bd 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -7800,6 +7800,41 @@ template <> otError Interpreter::Process(Arg aArgs[]) } } } + /** + * @cli trel counters + * @code + * trel counters + * Inbound: Packets 32 Bytes 4000 + * Outbound: Packets 4 Bytes 320 Failures 1 + * Done + * @endcode + * @par api_copy + * #otTrelGetCounters + */ + else if (aArgs[0] == "counters") + { + if (aArgs[1].IsEmpty()) + { + OutputTrelCounters(*otTrelGetCounters(GetInstancePtr())); + } + /** + * @cli trel counters reset + * @code + * trel counters reset + * Done + * @endcode + * @par api_copy + * #otTrelResetCounters + */ + else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty()) + { + otTrelResetCounters(GetInstancePtr()); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + } else { error = OT_ERROR_INVALID_ARGS; @@ -7808,6 +7843,19 @@ template <> otError Interpreter::Process(Arg aArgs[]) exit: return error; } + +void Interpreter::OutputTrelCounters(const otTrelCounters &aCounters) +{ + Uint64StringBuffer u64StringBuffer; + + OutputFormat("Inbound: Packets %s ", Uint64ToString(aCounters.mRxPackets, u64StringBuffer)); + OutputLine("Bytes %s", Uint64ToString(aCounters.mRxBytes, u64StringBuffer)); + + OutputFormat("Outbound: Packets %s ", Uint64ToString(aCounters.mTxPackets, u64StringBuffer)); + OutputFormat("Bytes %s ", Uint64ToString(aCounters.mTxBytes, u64StringBuffer)); + OutputLine("Failures %s", Uint64ToString(aCounters.mTxFailure, u64StringBuffer)); +} + #endif template <> otError Interpreter::Process(Arg aArgs[]) diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 6fb727607..7b687dfb0 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -483,6 +483,9 @@ private: void OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry); #endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + void OutputTrelCounters(const otTrelCounters &aCounters); +#endif #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE void OutputNat64Counters(const otNat64Counters &aCounters); #endif diff --git a/src/core/api/trel_api.cpp b/src/core/api/trel_api.cpp index 687c1169c..09344d3d1 100644 --- a/src/core/api/trel_api.cpp +++ b/src/core/api/trel_api.cpp @@ -60,6 +60,11 @@ const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *a return AsCoreType(aInstance).Get().GetNextPeer(*aIterator); } +uint16_t otTrelGetNumberOfPeers(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetNumberOfPeers(); +} + void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable) { AsCoreType(aInstance).Get().SetFilterEnabled(aEnable); @@ -70,4 +75,11 @@ bool otTrelIsFilterEnabled(otInstance *aInstance) return AsCoreType(aInstance).Get().IsFilterEnabled(); } +const otTrelCounters *otTrelGetCounters(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetCounters(); +} + +void otTrelResetCounters(otInstance *aInstance) { AsCoreType(aInstance).Get().ResetCounters(); } + #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp index d25347733..dc94b4511 100644 --- a/src/core/radio/trel_interface.cpp +++ b/src/core/radio/trel_interface.cpp @@ -352,6 +352,10 @@ void Interface::RemovePeerEntry(Peer &aEntry) mPeerTable.PopBack(); } +const Counters *Interface::GetCounters(void) const { return otPlatTrelGetCounters(&GetInstance()); } + +void Interface::ResetCounters(void) { otPlatTrelResetCounters(&GetInstance()); } + Error Interface::Send(const Packet &aPacket, bool aIsDiscovery) { Error error = kErrorNone; diff --git a/src/core/radio/trel_interface.hpp b/src/core/radio/trel_interface.hpp index 3d48368a5..c5750bcf8 100644 --- a/src/core/radio/trel_interface.hpp +++ b/src/core/radio/trel_interface.hpp @@ -59,6 +59,12 @@ class Link; extern "C" void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength); extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo); +/** + * Represents a group of TREL counters. + * + */ +typedef otTrelCounters Counters; + /** * Represents a TREL link interface. * @@ -205,6 +211,14 @@ public: */ const Peer *GetNextPeer(PeerIterator &aIterator) const; + /** + * Returns the number of TREL peers. + * + * @returns The number of TREL peers. + * + */ + uint16_t GetNumberOfPeers(void) const { return mPeerTable.GetLength(); } + /** * Sets the filter mode (enables/disables filtering). * @@ -228,6 +242,20 @@ public: */ bool IsFilterEnabled(void) const { return mFiltered; } + /** + * Gets the TREL counters. + * + * The counters are initialized to zero when the TREL platform is initialized. + * + */ + const Counters *GetCounters(void) const; + + /** + * Resets the TREL counters. + * + */ + void ResetCounters(void); + private: #if OPENTHREAD_COFNIG_TREL_PEER_TABLE_SIZE != 0 static constexpr uint16_t kPeerTableSize = OPENTHREAD_COFNIG_TREL_PEER_TABLE_SIZE; diff --git a/src/posix/platform/trel.cpp b/src/posix/platform/trel.cpp index 4e5ed53ec..ea06bca88 100644 --- a/src/posix/platform/trel.cpp +++ b/src/posix/platform/trel.cpp @@ -61,11 +61,12 @@ typedef struct TxPacket otSockAddr mDestSockAddr; } TxPacket; -static uint8_t sRxPacketBuffer[kMaxPacketSize]; -static uint16_t sRxPacketLength; -static TxPacket sTxPacketPool[OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE]; -static TxPacket *sFreeTxPacketHead; // A singly linked list of free/available `TxPacket` from pool. -static TxPacket *sTxPacketQueueTail; // A circular linked list for queued tx packets. +static uint8_t sRxPacketBuffer[kMaxPacketSize]; +static uint16_t sRxPacketLength; +static TxPacket sTxPacketPool[OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE]; +static TxPacket *sFreeTxPacketHead; // A singly linked list of free/available `TxPacket` from pool. +static TxPacket *sTxPacketQueueTail; // A circular linked list for queued tx packets. +static otPlatTrelCounters sCounters; static char sInterfaceName[IFNAMSIZ + 1]; static bool sInitialized = false; @@ -186,11 +187,19 @@ static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otSock error = OT_ERROR_INVALID_STATE; } } + else + { + ++sCounters.mTxPackets; + sCounters.mTxBytes += aLength; + } exit: otLogDebgPlat("[trel] SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort, otThreadErrorToString(error), BufferToString(aBuffer, aLength)); - + if (error != OT_ERROR_NONE) + { + ++sCounters.mTxFailure; + } return error; } @@ -218,6 +227,8 @@ static void ReceivePacket(int aSocket, otInstance *aInstance) if (sEnabled) { + ++sCounters.mRxPackets; + sCounters.mRxBytes += sRxPacketLength; otPlatTrelHandleReceived(aInstance, sRxPacketBuffer, sRxPacketLength); } } @@ -305,6 +316,8 @@ exit: return; } +static void ResetCounters() { memset(&sCounters, 0, sizeof(sCounters)); } + //--------------------------------------------------------------------------------------------------------------------- // trelDnssd // @@ -485,6 +498,21 @@ exit: return; } +// We keep counters at the platform layer because TREL failures can only be captured accurately within +// the platform layer as the platform sometimes only queues the packet and the packet will be sent later +// and the error is only known after sent. +const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return &sCounters; +} + +void otPlatTrelResetCounters(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + ResetCounters(); +} + //--------------------------------------------------------------------------------------------------------------------- // platformTrel system @@ -506,6 +534,8 @@ void platformTrelInit(const char *aTrelUrl) InitPacketQueue(); sInitialized = true; + + ResetCounters(); } void platformTrelDeinit(void) diff --git a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py index 33487d836..20ed03932 100755 --- a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py +++ b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py @@ -143,6 +143,23 @@ class TestTrelConnectivity(thread_cert.TestCase): self.assertTrue(med1.ping(router2_mleid)) self.assertTrue(sed1.ping(router2_mleid)) + counters = br1.get_trel_counters() + print('br1 trel counters', counters) + self.assertTrue(counters['Inbound']['packets'] > 0) + self.assertTrue(counters['Inbound']['bytes'] > 0) + self.assertTrue(counters['Outbound']['packets'] > 0) + self.assertTrue(counters['Outbound']['bytes'] > 0) + self.assertTrue(counters['Outbound']['failures'] >= 0) + + br1.reset_trel_counters() + counters = br1.get_trel_counters() + print('br1 trel counters after reset', counters) + self.assertTrue(counters['Inbound']['packets'] == 0) + self.assertTrue(counters['Inbound']['bytes'] == 0) + self.assertTrue(counters['Outbound']['packets'] == 0) + self.assertTrue(counters['Outbound']['bytes'] == 0) + self.assertTrue(counters['Outbound']['failures'] == 0) + def verify(self, pv: PacketVerifier): pkts: PacketFilter = pv.pkts BR1_RLOC16 = pv.vars['BR1_RLOC16'] diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 607d53214..b1b94082f 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1390,6 +1390,30 @@ class NodeImpl: raise + def get_trel_counters(self): + cmd = 'trel counters' + self.send_command(cmd) + result = self._expect_command_output() + + counters = {} + for line in result: + m = re.match(r'(\w+)\:[^\d]+(\d+)[^\d]+(\d+)(?:[^\d]+(\d+))?', line) + if m: + groups = m.groups() + sub_counters = { + 'packets': int(groups[1]), + 'bytes': int(groups[2]), + } + if groups[3]: + sub_counters['failures'] = int(groups[3]) + counters[groups[0]] = sub_counters + return counters + + def reset_trel_counters(self): + cmd = 'trel counters reset' + self.send_command(cmd) + self._expect_done() + def _encode_txt_entry(self, entry): """Encodes the TXT entry to the DNS-SD TXT record format as a HEX string. diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 361711428..2ede8e8e8 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -424,6 +424,10 @@ OT_TOOL_WEAK void otPlatTrelDisable(otInstance *) {} OT_TOOL_WEAK void otPlatTrelSend(otInstance *, const uint8_t *, uint16_t, const otSockAddr *) {} OT_TOOL_WEAK void otPlatTrelRegisterService(otInstance *, uint16_t, const uint8_t *, uint8_t) {} + +OT_TOOL_WEAK const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *) { return nullptr; } + +OT_TOOL_WEAK void otPlatTrelResetCounters(otInstance *) {} #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h index 97a9d79d9..7197feb7e 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -40,6 +40,7 @@ #include #include #include +#include #include "common/code_utils.hpp" #include "instance/instance.hpp"