[vendor-info] add vendor OUI support (#12991)

This commit introduces support for configuring and retrieving a vendor
OUI-24 (Organizationally Unique Identifier). It defines the new
`OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI` configuration option and adds
the `otThreadGetVendorOui()` and `otThreadSetVendorOui()` APIs.

When specified, the vendor OUI is included in the `BorderAgent`
mDNS/DNS-SD TXT data under the `vo` key.

The `VendorInfo` class is updated to manage the OUI value. This
commit also adds the `vendor oui` CLI command to get or set this
property. Finally, it updates the tests to validate the presence and
correctness of the new `vo` key in the TXT data.
This commit is contained in:
Abtin Keshavarzian
2026-04-29 15:09:43 -05:00
committed by GitHub
parent d37e9df698
commit 2cf0bdae62
12 changed files with 207 additions and 6 deletions
+1
View File
@@ -362,6 +362,7 @@ ot_int_option(OT_MLE_MAX_CHILDREN OPENTHREAD_CONFIG_MLE_MAX_CHILDREN "set maximu
ot_int_option(OT_RCP_RESTORATION_MAX_COUNT OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT "set max RCP restoration count")
ot_int_option(OT_RCP_TIME_SYNC_INTERVAL OPENTHREAD_SPINEL_CONFIG_RCP_TIME_SYNC_INTERVAL "set host-RCP time sync interval in microseconds")
ot_int_option(OT_RCP_TX_WAIT_TIME_SECS OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS "set RCP TX wait TIME in seconds")
ot_int_option(OT_VENDOR_OUI OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI "set the vendor OUI")
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (592)
#define OPENTHREAD_API_VERSION (593)
/**
* @addtogroup api-instance
+28
View File
@@ -425,6 +425,20 @@ const char *otThreadGetVendorSwVersion(otInstance *aInstance);
*/
const char *otThreadGetVendorAppUrl(otInstance *aInstance);
/**
* Represents an unspecified Vendor OUI.
*/
#define OT_THREAD_UNSPECIFIED_VENDOR_OUI (0xffffffff)
/**
* Get the vendor OUI-24
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
* @returns The vendor OUI-24 value in hex format, or `OT_THREAD_UNSPECIFIED_VENDOR_OUI` is not specified.
*/
uint32_t otThreadGetVendorOui(otInstance *aInstance);
/**
* Set the vendor name string.
*
@@ -494,6 +508,20 @@ otError otThreadSetVendorSwVersion(otInstance *aInstance, const char *aVendorSwV
*/
otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl);
/**
* Set the vendor OUI-24.
*
* Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aVendorOui The vendor OUI-24 value in Hexadecimal representation (e.g., OUI 64-16-66 is represented as
* `0x641666`). Must be a 24-bit value.
*
* @retval OT_ERROR_NONE Successfully set the vendor OUI.
* @retval OT_ERROR_INVALID_ARGS @p aVendorOui is not a valid 24-bit value.
*/
otError otThreadSetVendorOui(otInstance *aInstance, uint32_t aVendorOui);
/**
* Callback function pointer to notify when a Network Diagnostic Reset request message is received for the
* `OT_NETWORK_DIAGNOSTIC_TLV_NON_PREFERRED_CHANNELS` TLV.
+47
View File
@@ -7627,6 +7627,53 @@ template <> otError Interpreter::Process<Cmd("vendor")>(Arg aArgs[])
error = ProcessGetSet(aArgs, otThreadGetVendorAppUrl, otThreadSetVendorAppUrl);
#endif
}
/**
* @cli vendor oui
* @code
* vendor oui
* B4-A6-61
* Done
* @endcode
* @par api_copy
* #otThreadGetVendorOui
*/
else if (aArgs[0] == "oui")
{
if (aArgs[1].IsEmpty())
{
uint32_t oui = otThreadGetVendorOui(GetInstancePtr());
if (oui == OT_THREAD_UNSPECIFIED_VENDOR_OUI)
{
OutputLine("unspecified");
}
else
{
OutputLine("%02X-%02X-%02X", static_cast<uint8_t>((oui >> 16) & 0xff),
static_cast<uint8_t>((oui >> 8) & 0xff), static_cast<uint8_t>(oui & 0xff));
}
error = OT_ERROR_NONE;
}
else
{
#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
/**
* @cli vendor oui (set)
* @code
* vendor oui 0xb4a661
* Done
* @endcode
* @par api_copy
* #otThreadSetVendorOui
* @cparam vendor oui @ca{oui}
*/
error = ProcessSet(aArgs + 1, otThreadSetVendorOui);
#else
error = OT_ERROR_INVALID_ARGS;
#endif
}
}
return error;
}
+10 -1
View File
@@ -85,7 +85,10 @@ const char *otThreadGetVendorAppUrl(otInstance *aInstance)
return AsCoreType(aInstance).Get<VendorInfo>().GetAppUrl();
}
uint32_t otThreadGetVendorOui(otInstance *aInstance) { return AsCoreType(aInstance).Get<VendorInfo>().GetOui(); }
#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
otError otThreadSetVendorName(otInstance *aInstance, const char *aVendorName)
{
return AsCoreType(aInstance).Get<VendorInfo>().SetName(aVendorName);
@@ -105,7 +108,13 @@ otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl
{
return AsCoreType(aInstance).Get<VendorInfo>().SetAppUrl(aVendorAppUrl);
}
#endif
otError otThreadSetVendorOui(otInstance *aInstance, uint32_t aVendorOui)
{
return AsCoreType(aInstance).Get<VendorInfo>().SetOui(aVendorOui);
}
#endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
void otThreadSetNonPreferredChannels(otInstance *aInstance, otChannelMask aChannelMask)
{
+12
View File
@@ -88,6 +88,18 @@
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL ""
#endif
/**
* @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI
*
* Specifies the default Vendor OUI-24 value in Hexadecimal representation (e.g., OUI 64-16-66 is represented as
* `0x641666`).
*
* The value of `0xffffffff` (UINT32_MAX) is used to indicate OUI is not specified.
*/
#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI (0xffffffff)
#endif
/**
* @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
*
+31 -3
View File
@@ -69,6 +69,7 @@ TxtData::TxtData(Instance &aInstance)
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
, mShouldAddVendorName(true)
, mShouldAddVendorModel(true)
, mShouldAddVendorOui(true)
#endif
{
}
@@ -79,7 +80,8 @@ Error TxtData::Prepare(uint8_t *aBuffer,
uint16_t aBufferSize,
uint16_t &aLength,
bool aAddVendorName,
bool aAddVendorModel)
bool aAddVendorModel,
bool aAddVendorOui)
{
Error error = kErrorNone;
Dns::TxtDataEncoder encoder(aBuffer, aBufferSize);
@@ -169,6 +171,14 @@ Error TxtData::Prepare(uint8_t *aBuffer,
SuccessOrExit(error = encoder.AppendStringEntry(Key::kModelName, Get<VendorInfo>().GetModel()));
}
if (aAddVendorOui && Get<VendorInfo>().IsOuiSpecified())
{
uint8_t ouiData[kVendorOuiSize];
BigEndian::WriteUint24(Get<VendorInfo>().GetOui(), ouiData);
SuccessOrExit(error = encoder.AppendEntry(Key::kVendorOui, ouiData));
}
aLength = encoder.GetLength();
exit:
@@ -178,7 +188,7 @@ exit:
Error TxtData::Prepare(ServiceTxtData &aTxtData)
{
return Prepare(aTxtData.mData, sizeof(aTxtData.mData), aTxtData.mLength, /* aAddVendorName */ false,
/* aAddVendorModel */ false);
/* aAddVendorModel */ false, /* aAddVendorOui */ false);
}
void TxtData::SetChangedCallback(ChangedCallback aCallback, void *aContext)
@@ -236,11 +246,16 @@ void TxtData::PrepareWithVendorData(Heap::Data &aTxtData)
size += Tlv::kMaxVendorModelLength + sizeof(Key::kModelName) + sizeof('=');
}
if (mShouldAddVendorOui)
{
size += kVendorOuiSize + sizeof(Key::kVendorOui) + sizeof('=');
}
buffer = reinterpret_cast<uint8_t *>(Heap::CAlloc(size, sizeof(uint8_t)));
OT_ASSERT(buffer != nullptr);
SuccessOrAssert(Prepare(buffer, size, length, mShouldAddVendorName, mShouldAddVendorModel));
SuccessOrAssert(Prepare(buffer, size, length, mShouldAddVendorName, mShouldAddVendorModel, mShouldAddVendorOui));
if (mVendorData.GetLength() != 0)
{
@@ -266,6 +281,7 @@ void TxtData::SetVendorData(const uint8_t *aVendorData, uint16_t aVendorDataLeng
mShouldAddVendorName = true;
mShouldAddVendorModel = true;
mShouldAddVendorOui = true;
iterator.Init(mVendorData.GetBytes(), mVendorData.GetLength());
@@ -279,6 +295,10 @@ void TxtData::SetVendorData(const uint8_t *aVendorData, uint16_t aVendorDataLeng
{
mShouldAddVendorModel = false;
}
else if (entry.MatchesKey(Key::kVendorOui))
{
mShouldAddVendorOui = false;
}
}
Refresh();
@@ -303,6 +323,14 @@ void TxtData::HandleVendorModelChange(void)
}
}
void TxtData::HandleVendorOuiChange(void)
{
if (mShouldAddVendorOui)
{
Refresh();
}
}
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
uint32_t TxtData::StateBitmap::Determine(Instance &aInstance)
+9 -1
View File
@@ -279,6 +279,7 @@ public:
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
private:
static constexpr uint8_t kVendorOuiSize = OT_BORDER_AGENT_VENDOR_OUI_SIZE;
static constexpr uint16_t kMaxSizeNoVendorData = OT_BORDER_AGENT_MESHCOP_SERVICE_TXT_DATA_MAX_LENGTH;
static const char kRecordVersion[];
@@ -358,7 +359,12 @@ private:
#endif
};
Error Prepare(uint8_t *aBuffer, uint16_t aBufferSize, uint16_t &aLength, bool aAddVendorName, bool aAddVendorModel);
Error Prepare(uint8_t *aBuffer,
uint16_t aBufferSize,
uint16_t &aLength,
bool aAddVendorName,
bool aAddVendorModel,
bool aAddVendorOui);
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
// Callback from Notifier
@@ -366,6 +372,7 @@ private:
// Callbacks from VendorInfo
void HandleVendorNameChange(void);
void HandleVendorModelChange(void);
void HandleVendorOuiChange(void);
void HandleChangedTask(void);
@@ -377,6 +384,7 @@ private:
Heap::Data mVendorData;
bool mShouldAddVendorName;
bool mShouldAddVendorModel;
bool mShouldAddVendorOui;
#endif
#endif
};
+20
View File
@@ -57,11 +57,14 @@ VendorInfo::VendorInfo(Instance &aInstance)
static_assert(sizeof(kSwVersion) <= sizeof(SwVersionStringType), "VENDOR_SW_VERSION is too long");
static_assert(sizeof(kAppUrl) <= sizeof(AppUrlStringType), "VENDOR_APP_URL is too long");
static_assert((kOui == kUnspecifiedOui) || ((kOui & ~kOuiMask) == 0), "VENDOR_OUI is invalid, MUST be 24-bit");
#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
memcpy(mName, kName, sizeof(kName));
memcpy(mModel, kModel, sizeof(kModel));
memcpy(mSwVersion, kSwVersion, sizeof(kSwVersion));
memcpy(mAppUrl, kAppUrl, sizeof(kAppUrl));
mOui = kOui;
#endif
}
@@ -110,6 +113,23 @@ Error VendorInfo::SetSwVersion(const char *aSwVersion)
Error VendorInfo::SetAppUrl(const char *aAppUrl) { return StringCopy(mAppUrl, aAppUrl, kStringCheckUtf8Encoding); }
Error VendorInfo::SetOui(uint32_t aOui)
{
Error error = kErrorNone;
VerifyOrExit((aOui & ~kOuiMask) == 0, error = kErrorInvalidArgs);
VerifyOrExit(aOui != mOui);
mOui = aOui;
#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
Get<MeshCoP::BorderAgent::TxtData>().HandleVendorOuiChange();
#endif
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
} // namespace ot
+39
View File
@@ -36,6 +36,8 @@
#include "openthread-core-config.h"
#include <openthread/netdiag.h>
#include "common/error.hpp"
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
@@ -49,6 +51,11 @@ namespace ot {
class VendorInfo : public InstanceLocator, private NonCopyable
{
public:
/**
* Unspecified Vendor OUI value.
*/
static constexpr uint32_t kUnspecifiedOui = OT_THREAD_UNSPECIFIED_VENDOR_OUI;
/**
* Initializes the `VendorInfo`.
*
@@ -130,14 +137,45 @@ public:
*/
Error SetAppUrl(const char *aAppUrl);
/**
* Indicates whether the Vendor OUI is specified.
*
* @retval TRUE The Vendor OUI is specified.
* @retval FALSE The Vendor OUI is not specified.
*/
bool IsOuiSpecified(void) const { return mOui != kUnspecifiedOui; }
/**
* Returns the Vendor OUI-24.
*
* @returns The Vendor OUI-24 value. Returns `kUnspecifiedOui` if it is not set.
*/
uint32_t GetOui(void) const { return mOui; }
/**
* Sets the Vendor OUI-24.
*
* @param[in] aOui The Vendor OUI-24 value in Hexadecimal representation (e.g., OUI 64-16-66 is represented as
* `0x641666`). Must be a 24-bit value.
*
* @retval kErrorNone Successfully set the Vendor OUI.
* @retval kErrorInvalidArgs @p aOui is not a valid 24-bit value.
*/
Error SetOui(uint32_t aOui);
#else
const char *GetName(void) const { return kName; }
const char *GetModel(void) const { return kModel; }
const char *GetSwVersion(void) const { return kSwVersion; }
const char *GetAppUrl(void) const { return kAppUrl; }
uint32_t GetOui(void) const { return kOui; }
bool IsOuiSpecified(void) const { return kOui != kUnspecifiedOui; }
#endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
private:
static constexpr uint32_t kOui = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI;
static constexpr uint32_t kOuiMask = 0xffffff;
// String buffer types (max size specified by corresponding TLV)
typedef NetworkDiagnostic::VendorNameTlv::StringType NameStringType;
typedef NetworkDiagnostic::VendorModelTlv::StringType ModelStringType;
@@ -154,6 +192,7 @@ private:
ModelStringType mModel;
SwVersionStringType mSwVersion;
AppUrlStringType mAppUrl;
uint32_t mOui;
#endif
};
@@ -125,6 +125,7 @@
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL "Nexus Simulation"
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME "RD:OpenThread by Google Nest"
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION "OT-simul-nexus"
#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_OUI 0x020100
#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE 1
#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 256
#define OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL (10 * 60)
+8
View File
@@ -1461,6 +1461,14 @@ void ValidateMeshCoPTxtData(TxtData &aTxtData, Node &aNode, bool aExpectVendorIn
aTxtData.ValidateKey("mn", expectedModelName);
VerifyOrQuit(info.mHasModelName);
VerifyOrQuit(StringMatch(info.mModelName, expectedModelName));
if (aNode.Get<VendorInfo>().IsOuiSpecified())
{
uint8_t ouiData[3];
BigEndian::WriteUint24(aNode.Get<VendorInfo>().GetOui(), ouiData);
aTxtData.ValidateKey("vo", ouiData);
}
}
else
{