From 593fc53fcfa006d853eddea9cc310f9857ef1510 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 24 Jun 2026 23:17:59 -0700 Subject: [PATCH] [netdiag] add support for Vendor OUI TLV (#13260) This commit introduces the Vendor OUI Network Diagnostic TLV (type 44), enabling devices to query and report their Vendor Organizationally Unique Identifier (OUI). Specifically, this commit implements TLV generation and parsing in the Network Diagnostics `Client` and `Server` modules, exposing the parsed OUI through public client APIs. It also updates the `networkdiagnostic` CLI commands to support querying and outputting Vendor OUI information. It also extends the existing toranj tests to cover the new TLV (`test-020-net-diag-vendor-info.py`). --- include/openthread/instance.h | 2 +- include/openthread/netdiag.h | 2 ++ src/cli/cli.cpp | 5 +++++ src/core/thread/net_diag.cpp | 13 +++++++++++++ src/core/thread/net_diag_tlvs.hpp | 6 ++++++ src/core/thread/vendor_info.cpp | 14 ++++++++++++++ src/core/thread/vendor_info.hpp | 14 ++++++++++++++ tests/toranj/cli/cli.py | 6 ++++++ tests/toranj/cli/test-020-net-diag-vendor-info.py | 10 ++++++++++ 9 files changed, 71 insertions(+), 1 deletion(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 01539afcc..1a9f14415 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (608) +#define OPENTHREAD_API_VERSION (609) /** * @addtogroup api-instance diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h index 5a8511585..d3e76acc5 100644 --- a/include/openthread/netdiag.h +++ b/include/openthread/netdiag.h @@ -94,6 +94,7 @@ extern "C" { #define OT_NETWORK_DIAGNOSTIC_TLV_BR_DHCP6_PD_OMR_PREFIX 41 ///< Border Router DHCPv6-PD OMR Prefix TLV #define OT_NETWORK_DIAGNOSTIC_TLV_BR_LOCAL_OL_PREFIX 42 ///< Border Router Local On-link Prefix TLV #define OT_NETWORK_DIAGNOSTIC_TLV_BR_FAVORED_OL_PREFIX 43 ///< Border Router Favored On-link Prefix TLV +#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_OUI 44 ///< Vendor OUI TLV #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH 32 ///< Max length of Vendor Name TLV. #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH 32 ///< Max length of Vendor Model TLV. @@ -348,6 +349,7 @@ typedef struct otNetworkDiagTlv char mVendorSwVersion[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH + 1]; char mThreadStackVersion[OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH + 1]; char mVendorAppUrl[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH + 1]; + otThreadVendorOui mVendorOui; otChannelMask mNonPreferredChannels; otNetworkDiagData mChannelPages; otNetworkDiagChildTable mChildTable; diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 67a6f4450..53b01d023 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -7757,6 +7757,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) * - `41`: Border Router DHCPv6-PD OMR Prefix TLV * - `42`: Border Router Local On-link Prefix TLV * - `43`: Border Router Favored On-link Prefix TLV + * - `44`: Vendor OUI TLV * * @par * Sends a network diagnostic request to retrieve specified Type Length Values (TLVs) @@ -7927,6 +7928,10 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL: OutputLine("Vendor App URL: %s", diagTlv.mData.mVendorAppUrl); break; + case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_OUI: + OutputFormat("Vendor OUI: "); + OutputVendorOuiLine(diagTlv.mData.mVendorOui); + break; case OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION: OutputLine("Thread Stack Version: %s", diagTlv.mData.mThreadStackVersion); break; diff --git a/src/core/thread/net_diag.cpp b/src/core/thread/net_diag.cpp index 7b6ce8a14..36117cd64 100644 --- a/src/core/thread/net_diag.cpp +++ b/src/core/thread/net_diag.cpp @@ -406,6 +406,15 @@ Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage) error = Tlv::Append(aMessage, Get().GetAppUrl()); break; + case Tlv::kVendorOui: + { + const VendorInfo::Oui &oui = Get().GetOui(); + + VerifyOrExit(oui.IsValid()); + error = Tlv::Append(aMessage, oui.GetBytes(), oui.GetSize()); + break; + } + case Tlv::kThreadStackVersion: error = Tlv::Append(aMessage, otGetVersionString()); break; @@ -1182,6 +1191,10 @@ Error Client::ParseDiagTlv(const Message &aMessage, const Tlv::Info &aTlvInfo, D error = aTlvInfo.Read(aMessage, aDiagTlv.mData.mVendorAppUrl); break; + case Tlv::kVendorOui: + error = AsCoreType(&aDiagTlv.mData.mVendorOui).ParseFrom(aMessage, aTlvInfo.GetValueOffsetRange()); + break; + case Tlv::kThreadStackVersion: error = aTlvInfo.Read(aMessage, aDiagTlv.mData.mThreadStackVersion); break; diff --git a/src/core/thread/net_diag_tlvs.hpp b/src/core/thread/net_diag_tlvs.hpp index 3ed1b40ec..885572905 100644 --- a/src/core/thread/net_diag_tlvs.hpp +++ b/src/core/thread/net_diag_tlvs.hpp @@ -105,6 +105,7 @@ public: kBrDhcp6PdOmrPrefix = OT_NETWORK_DIAGNOSTIC_TLV_BR_DHCP6_PD_OMR_PREFIX, kBrLocalOnlinkPrefix = OT_NETWORK_DIAGNOSTIC_TLV_BR_LOCAL_OL_PREFIX, kBrFavoredOnLinkPrefix = OT_NETWORK_DIAGNOSTIC_TLV_BR_FAVORED_OL_PREFIX, + kVendorOui = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_OUI, }; /** @@ -233,6 +234,11 @@ typedef StringTlvInfo VendorAppUrlTlv; +/** + * Defines Vendor OUI TLV constants and types. + */ +typedef TlvInfo VendorOuiTlv; + /** * Defines Child IPv6 Address List TLV constants and types. */ diff --git a/src/core/thread/vendor_info.cpp b/src/core/thread/vendor_info.cpp index dfa536029..20a4ca304 100644 --- a/src/core/thread/vendor_info.cpp +++ b/src/core/thread/vendor_info.cpp @@ -187,6 +187,20 @@ exit: return error; } +Error VendorInfo::Oui::ParseFrom(const Message &aMessage, OffsetRange aOffsetRange) +{ + Error error = kErrorParse; + + aOffsetRange.ShrinkLength(kMaxSize); + SuccessOrExit(SetBitLengthFromSize(aOffsetRange.GetLength())); + + SuccessOrExit(error = aMessage.Read(aOffsetRange.GetOffset(), mBytes, GetSize())); + Tidy(); + +exit: + return error; +} + void VendorInfo::Oui::Tidy(void) { uint8_t index; diff --git a/src/core/thread/vendor_info.hpp b/src/core/thread/vendor_info.hpp index f6e3cc1c2..dff4203c2 100644 --- a/src/core/thread/vendor_info.hpp +++ b/src/core/thread/vendor_info.hpp @@ -44,6 +44,7 @@ #include "common/equatable.hpp" #include "common/error.hpp" #include "common/locator.hpp" +#include "common/message.hpp" #include "common/non_copyable.hpp" #include "common/string.hpp" #include "thread/net_diag_tlvs.hpp" @@ -140,6 +141,19 @@ public: */ Error SetFrom(const uint8_t *aData, uint16_t aSize); + /** + * Parses the OUI from a message. + * + * If @p aOffsetRange is longer than `kMaxSize`, the additional bytes beyond `kMaxSize` are ignored. + * + * @param[in] aMessage The message to read from. + * @param[in] aOffsetRange The offset range in @p aMessage to read from. + * + * @retval kErrorNone Successfully parsed the OUI from @p aMessage. + * @retval kErrorParse Failed to parse the OUI from @p aMessage. + */ + Error ParseFrom(const Message &aMessage, OffsetRange aOffsetRange); + /** * Returns the OUI as a 24-bit OUI value. * diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index aec0b9dc8..d1c42b27a 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -421,6 +421,12 @@ class Node(object): def set_vendor_app_url(self, url): return self._cli_no_output('vendor appurl', url) + def get_vendor_oui(self): + return self._cli_single_output('vendor oui') + + def set_vendor_oui(self, oui): + return self._cli_no_output('vendor oui', oui) + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # netdata diff --git a/tests/toranj/cli/test-020-net-diag-vendor-info.py b/tests/toranj/cli/test-020-net-diag-vendor-info.py index a25e50743..141f8bd6f 100755 --- a/tests/toranj/cli/test-020-net-diag-vendor-info.py +++ b/tests/toranj/cli/test-020-net-diag-vendor-info.py @@ -70,6 +70,7 @@ VENDOR_SW_VERSION_TLV = 27 THREAD_STACK_VERSION_TLV = 28 MLE_COUNTERS_TLV = 34 VENDOR_APP_URL = 35 +VENDOR_OUI = 44 NON_PREFERRED_CHANNELS_TLV = 36 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -79,11 +80,13 @@ r1.set_vendor_name('RD:nest') r1.set_vendor_model('marble') r1.set_vendor_sw_version('ot-1.4') r1.set_vendor_app_url('https://example.com/vendor-app') +r1.set_vendor_oui(0x1a2b3c) verify(r1.get_vendor_name() == 'RD:nest') verify(r1.get_vendor_model() == 'marble') verify(r1.get_vendor_sw_version() == 'ot-1.4') verify(r1.get_vendor_app_url() == 'https://example.com/vendor-app') +verify(r1.get_vendor_oui() == '1A-2B-3C') #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Check invalid names (too long) @@ -178,6 +181,13 @@ verify(len(result) == 2) verify(result[1].startswith("Vendor App URL:")) verify(result[1].split(':', 1)[1].strip() == r1.get_vendor_app_url()) +# Get vendor OUI (TLV 44) + +result = r2.cli('networkdiagnostic get', r1_rloc, VENDOR_OUI) +verify(len(result) == 2) +verify(result[1].startswith("Vendor OUI:")) +verify(result[1].split(':', 1)[1].strip() == r1.get_vendor_oui()) + # Get thread stack version (TLV 30) result = r2.cli('networkdiagnostic get', r1_rloc, THREAD_STACK_VERSION_TLV)