From 292d6d525e345e6d9e21a81cf5d79e342ae4171b Mon Sep 17 00:00:00 2001 From: Przemyslaw Bida Date: Mon, 5 Feb 2024 21:58:41 +0100 Subject: [PATCH] [tcat] Implement tcat advertisement. Commit introduces implementation of TCAT advertisement over BLE compialnt with Thread 1.3.1 specification. --- examples/platforms/simulation/ble.c | 22 ++++ include/openthread/ble_secure.h | 13 +- include/openthread/instance.h | 2 +- include/openthread/platform/ble.h | 50 +++++++ include/openthread/tcat.h | 44 +++++-- src/cli/cli_tcat.cpp | 93 ++++++++++++- src/core/api/ble_secure_api.cpp | 9 +- src/core/meshcop/meshcop.cpp | 1 + src/core/meshcop/tcat_agent.cpp | 122 ++++++++++++++++-- src/core/meshcop/tcat_agent.hpp | 80 ++++++++++-- src/core/radio/ble_secure.cpp | 14 +- src/core/radio/ble_secure.hpp | 14 +- src/posix/platform/ble.cpp | 23 ++++ .../scripts/expect/cli-tcat-advertisement.exp | 75 +++++++++++ tests/unit/test_platform.cpp | 24 ++++ tests/unit/test_tcat.cpp | 5 +- 16 files changed, 543 insertions(+), 48 deletions(-) create mode 100755 tests/scripts/expect/cli-tcat-advertisement.exp diff --git a/examples/platforms/simulation/ble.c b/examples/platforms/simulation/ble.c index d48922ac9516..2e89e074689f 100644 --- a/examples/platforms/simulation/ble.c +++ b/examples/platforms/simulation/ble.c @@ -214,3 +214,25 @@ OT_TOOL_WEAK void otPlatBleGattServerOnWriteRequest(otInstance *aIns * which is available in FTD/MTD library. */ } + +void otPlatBleGetLinkCapabilities(otInstance *aInstance, otBleLinkCapabilities *aBleLinkCapabilities) +{ + OT_UNUSED_VARIABLE(aInstance); + aBleLinkCapabilities->mGattNotifications = 1; + aBleLinkCapabilities->mL2CapDirect = 0; + aBleLinkCapabilities->mRsv = 0; +} + +otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aAdvertisementData); + OT_UNUSED_VARIABLE(aAdvertisementLen); + return OT_ERROR_NONE; +} + +bool otPlatBleSupportsMultiRadio(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return false; +} diff --git a/include/openthread/ble_secure.h b/include/openthread/ble_secure.h index 98d015640e34..ca0e8706f247 100644 --- a/include/openthread/ble_secure.h +++ b/include/openthread/ble_secure.h @@ -109,6 +109,17 @@ otError otBleSecureStart(otInstance *aInstance, bool aTlvMode, void *aContext); +/** + * Sets TCAT vendor info + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method call. + * + * @retval OT_ERROR_NONE Successfully set value. + * @retval OT_ERROR_INVALID_ARGS Value not set. + */ +otError otBleSecureSetTcatVendorInfo(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo); + /** * Enables the TCAT protocol over BLE Secure. * @@ -122,7 +133,7 @@ otError otBleSecureStart(otInstance *aInstance, * @retval OT_ERROR_INVALID_STATE The BLE function has not been started or line mode is not selected. * */ -otError otBleSecureTcatStart(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo, otHandleTcatJoin aHandler); +otError otBleSecureTcatStart(otInstance *aInstance, otHandleTcatJoin aHandler); /** * Stops the BLE Secure server. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 8e912e66c6bd..1964b22d002d 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 (413) +#define OPENTHREAD_API_VERSION (414) /** * @addtogroup api-instance diff --git a/include/openthread/platform/ble.h b/include/openthread/platform/ble.h index bf7e48bc42f3..d8930cf66862 100644 --- a/include/openthread/platform/ble.h +++ b/include/openthread/platform/ble.h @@ -104,6 +104,23 @@ extern "C" { #define OT_BLE_DEFAULT_POWER 0 +/** + * TOBLE service UUID + */ + +#define OT_TOBLE_SERVICE_UUID 0xfffb + +/** + * Represent BLE link capabilities + * + */ +typedef struct otBleLinkCapabilities +{ + uint8_t mRsv : 6; + bool mL2CapDirect : 1; + bool mGattNotifications : 1; +} otBleLinkCapabilities; + /** * Represents a BLE packet. * @@ -152,6 +169,23 @@ otError otPlatBleDisable(otInstance *aInstance); * @section Bluetooth Low Energy GAP. ***************************************************************************/ +/** + * Sets BLE Advertising data. + * + * + * @note This function shall be used only for BLE Peripheral role. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aAdvertisementData The formatted TCAT advertisement frame. + * @param[in] aAdvertisementLen The TCAT advertisement frame length. + * + * @retval OT_ERROR_NONE Advertising procedure has been started. + * @retval OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * @retval OT_ERROR_INVALID_ARGS Invalid value has been supplied. + * + */ +otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen); + /** * Starts BLE Advertising procedure. * @@ -281,6 +315,22 @@ otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, con */ extern void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket); +/** + * Function to retrieve from platform BLE link capabilities. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aBleLinkCapabilities The pointer to retrieve the BLE ling capabilities. + * + */ +void otPlatBleGetLinkCapabilities(otInstance *aInstance, otBleLinkCapabilities *aBleLinkCapabilities); + +/** + * Function to retrieve from platform multiradio support of BLE and IEEE. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +bool otPlatBleSupportsMultiRadio(otInstance *aInstance); /** * @} * diff --git a/include/openthread/tcat.h b/include/openthread/tcat.h index 291ce4817f68..d1b9c8c45e30 100644 --- a/include/openthread/tcat.h +++ b/include/openthread/tcat.h @@ -67,6 +67,10 @@ extern "C" { #define OT_TCAT_MAX_SERVICE_NAME_LENGTH \ 15 ///< Maximum string length of a UDP or TCP service name (does not include null char). +#define OT_TCAT_ADVERTISEMENT_MAX_LEN 29 ///< Maximum length of TCAT advertisement. +#define OT_TCAT_OPCODE 0x2 ///< TCAT Advertisement Operation Code. +#define OT_TCAT_MAX_VENDORID_SIZE 5 ///< TCAT max size of vendor ID including null as termination. + /** * Represents TCAT status code. * @@ -111,6 +115,27 @@ typedef enum otTcatCommandClass } otTcatCommandClass; +/** + * Represents Device ID type. + * + */ +typedef enum otTcatDeviceIdType +{ + OT_TCAT_DEVICE_ID_EMPTY = 0, ///< Vendor device ID type not set + OT_TCAT_DEVICE_ID_OUI24 = 1, ///< Vendor device ID type IEEE OUI-24 + OT_TCAT_DEVICE_ID_OUI36 = 2, ///< Vendor device ID type IEEE OUI-36 + OT_TCAT_DEVICE_ID_DISCRIMINATOR = 3, ///< Vendor device ID type Device Discriminator + OT_TCAT_DEVICE_ID_IANAPEN = 4, ///< Vendor device ID type IANA PEN + OT_TCAT_DEVICE_ID_MAX = 5, ///< Vendor device ID type size +} otTcatDeviceIdType; + +typedef struct otTcatDeviceId +{ + otTcatDeviceIdType mDeviceIdType; + uint16_t mDeviceIdLen; + uint8_t mDeviceId[OT_TCAT_MAX_VENDORID_SIZE]; +} otTcatDeviceId; + /** * This structure represents a TCAT vendor information. * @@ -119,15 +144,16 @@ typedef enum otTcatCommandClass */ typedef struct otTcatVendorInfo { - const char *mProvisioningUrl; ///< Provisioning URL path string - const char *mVendorName; ///< Vendor name string - const char *mVendorModel; ///< Vendor model string - const char *mVendorSwVersion; ///< Vendor software version string - const char *mVendorData; ///< Vendor specific data string - const char *mPskdString; ///< Vendor managed pre-shared key for device - const char *mInstallCode; ///< Vendor managed install code string - const char *mDeviceId; ///< Vendor managed device ID string (if NULL: device ID is set to EUI-64 in binary format) - + const char *mProvisioningUrl; ///< Provisioning URL path string + const char *mVendorName; ///< Vendor name string + const char *mVendorModel; ///< Vendor model string + const char *mVendorSwVersion; ///< Vendor software version string + const char *mVendorData; ///< Vendor specific data string + const char *mPskdString; ///< Vendor managed pre-shared key for device + const char *mInstallCode; ///< Vendor managed install code string + const otTcatDeviceId *mDeviceIds; /** Vendor managed device ID array. + (if NULL: device ID is set to EUI-64 in binary format) + Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */ } otTcatVendorInfo; /** diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp index 1ef73a561f7e..0126659671a4 100644 --- a/src/cli/cli_tcat.cpp +++ b/src/cli/cli_tcat.cpp @@ -31,10 +31,12 @@ #include "cli/cli_utils.hpp" #include "cli/cli_tcat.hpp" +#include "common/code_utils.hpp" #include #include +#include #include #include @@ -79,7 +81,9 @@ namespace ot { namespace Cli { -const char kPskdVendor[] = "J01NM3"; +otTcatDeviceId sVendorDeviceIds[OT_TCAT_DEVICE_ID_MAX]; + +const char kPskdVendor[] = "JJJJJJ"; const char kUrl[] = "dummy_url"; static void HandleBleSecureReceive(otInstance *aInstance, @@ -99,7 +103,8 @@ static void HandleBleSecureReceive(otInstance *aInstance, uint16_t nLen; uint8_t buf[kTextMaxLen]; - nLen = otMessageRead(aMessage, (uint16_t)aOffset, buf + kBufPrefixLen, sizeof(buf) - kBufPrefixLen - 1); + nLen = + otMessageRead(aMessage, static_cast(aOffset), buf + kBufPrefixLen, sizeof(buf) - kBufPrefixLen - 1); memcpy(buf, "RECV:", kBufPrefixLen); @@ -109,6 +114,85 @@ static void HandleBleSecureReceive(otInstance *aInstance, IgnoreReturnValue(otBleSecureFlush(aInstance)); } +template <> otError Tcat::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + otTcatDeviceId devId; + static const char *const kVendorIdTypes[] = {"empty", "oui24", "oui36", "discriminator", "ianapen"}; + + mVendorInfo.mDeviceIds = sVendorDeviceIds; + + if (aArgs[0].IsEmpty()) + { + if (mVendorInfo.mDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) + { + OutputLine("Set vendorIds:"); + for (size_t i = 0; mVendorInfo.mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) + { + OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mDeviceIds[i].mDeviceIdType]); + OutputBytesLine(const_cast(mVendorInfo.mDeviceIds[i].mDeviceId), + mVendorInfo.mDeviceIds[i].mDeviceIdLen); + } + } + else + { + OutputLine("%s", kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY]); + } + ExitNow(); + } + + if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_OUI24]) + { + devId.mDeviceIdType = OT_TCAT_DEVICE_ID_OUI24; + } + else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_OUI36]) + { + devId.mDeviceIdType = OT_TCAT_DEVICE_ID_OUI36; + } + else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_DISCRIMINATOR]) + { + devId.mDeviceIdType = OT_TCAT_DEVICE_ID_DISCRIMINATOR; + } + else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_IANAPEN]) + { + devId.mDeviceIdType = OT_TCAT_DEVICE_ID_IANAPEN; + } + else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY]) + { + for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + { + vendorDeviceId.mDeviceIdType = OT_TCAT_DEVICE_ID_EMPTY; + vendorDeviceId.mDeviceIdLen = 0; + } + ExitNow(); + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_VENDORID_SIZE * 2 + 1)) + { + devId.mDeviceIdLen = OT_TCAT_MAX_VENDORID_SIZE; + SuccessOrExit(error = aArgs[1].ParseAsHexString(devId.mDeviceIdLen, devId.mDeviceId)); + for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + { + if (vendorDeviceId.mDeviceIdType == devId.mDeviceIdType || + vendorDeviceId.mDeviceIdType == OT_TCAT_DEVICE_ID_EMPTY) + { + vendorDeviceId = devId; + break; + } + } + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } +exit: + return error; +} + template <> otError Tcat::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -129,8 +213,9 @@ template <> otError Tcat::Process(Arg aArgs[]) otBleSecureSetSslAuthMode(GetInstancePtr(), true); + SuccessOrExit(error = otBleSecureSetTcatVendorInfo(GetInstancePtr(), &mVendorInfo)); SuccessOrExit(error = otBleSecureStart(GetInstancePtr(), nullptr, HandleBleSecureReceive, true, nullptr)); - SuccessOrExit(error = otBleSecureTcatStart(GetInstancePtr(), &mVendorInfo, nullptr)); + SuccessOrExit(error = otBleSecureTcatStart(GetInstancePtr(), nullptr)); exit: return error; @@ -152,7 +237,7 @@ otError Tcat::Process(Arg aArgs[]) aCommandString, &Tcat::Process \ } - static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop")}; + static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop"), CmdEntry("vendorid")}; static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); diff --git a/src/core/api/ble_secure_api.cpp b/src/core/api/ble_secure_api.cpp index d30a7f0aa699..7130f6fc625c 100644 --- a/src/core/api/ble_secure_api.cpp +++ b/src/core/api/ble_secure_api.cpp @@ -55,9 +55,14 @@ otError otBleSecureStart(otInstance *aInstance, return AsCoreType(aInstance).Get().Start(aConnectHandler, aReceiveHandler, aTlvMode, aContext); } -otError otBleSecureTcatStart(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo, otHandleTcatJoin aHandler) +otError otBleSecureSetTcatVendorInfo(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo) { - return AsCoreType(aInstance).Get().TcatStart(AsCoreType(aVendorInfo), aHandler); + return AsCoreType(aInstance).Get().TcatSetVendorInfo(AsCoreType(aVendorInfo)); +} + +otError otBleSecureTcatStart(otInstance *aInstance, otHandleTcatJoin aHandler) +{ + return AsCoreType(aInstance).Get().TcatStart(aHandler); } void otBleSecureStop(otInstance *aInstance) { AsCoreType(aInstance).Get().Stop(); } diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index 8bce745829a7..3426e02c3bc1 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -33,6 +33,7 @@ */ #include "meshcop.hpp" +#include #include "common/clearable.hpp" #include "common/crc16.hpp" diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 1221a55492ac..dfc5c1778d7d 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -32,6 +32,11 @@ */ #include "tcat_agent.hpp" +#include +#include "common/as_core_type.hpp" +#include "common/error.hpp" +#include "common/message.hpp" +#include "common/tlvs.hpp" #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE @@ -64,7 +69,6 @@ TcatAgent::TcatAgent(Instance &aInstance) , mVendorInfo(nullptr) , mCurrentApplicationProtocol(kApplicationProtocolNone) , mState(kStateDisabled) - , mAlreadyCommissioned(false) , mCommissionerHasNetworkName(false) , mCommissionerHasDomainName(false) , mCommissionerHasExtendedPanId(false) @@ -73,25 +77,17 @@ TcatAgent::TcatAgent(Instance &aInstance) mCurrentServiceName[0] = 0; } -Error TcatAgent::Start(const TcatAgent::VendorInfo &aVendorInfo, - AppDataReceiveCallback aAppDataReceiveCallback, - JoinCallback aHandler, - void *aContext) +Error TcatAgent::Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext) { Error error = kErrorNone; LogInfo("Starting"); - - VerifyOrExit(aVendorInfo.IsValid(), error = kErrorInvalidArgs); - SuccessOrExit(error = mJoinerPskd.SetFrom(aVendorInfo.mPskdString)); - + VerifyOrExit(mVendorInfo != nullptr, error = kErrorFailed); mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext); mJoinCallback.Set(aHandler, aContext); - mVendorInfo = &aVendorInfo; mCurrentApplicationProtocol = kApplicationProtocolNone; mState = kStateEnabled; - mAlreadyCommissioned = false; exit: LogWarnOnError(error, "start TCAT agent"); @@ -102,12 +98,23 @@ void TcatAgent::Stop(void) { mCurrentApplicationProtocol = kApplicationProtocolNone; mState = kStateDisabled; - mAlreadyCommissioned = false; mAppDataReceiveCallback.Clear(); mJoinCallback.Clear(); LogInfo("TCAT agent stopped"); } +Error TcatAgent::SetTcatVendorInfo(const VendorInfo &aVendorInfo) +{ + Error error = kErrorNone; + + VerifyOrExit(aVendorInfo.IsValid(), error = kErrorInvalidArgs); + SuccessOrExit(error = mJoinerPskd.SetFrom(aVendorInfo.mPskdString)); + mVendorInfo = &aVendorInfo; + +exit: + return error; +} + Error TcatAgent::Connected(MeshCoP::SecureTransport &aTlsContext) { size_t len; @@ -160,7 +167,6 @@ Error TcatAgent::Connected(MeshCoP::SecureTransport &aTlsContext) mCurrentApplicationProtocol = kApplicationProtocolNone; mCurrentServiceName[0] = 0; mState = kStateConnected; - mAlreadyCommissioned = Get().IsCommissioned(); LogInfo("TCAT agent connected"); exit: @@ -170,7 +176,6 @@ Error TcatAgent::Connected(MeshCoP::SecureTransport &aTlsContext) void TcatAgent::Disconnected(void) { mCurrentApplicationProtocol = kApplicationProtocolNone; - mAlreadyCommissioned = false; if (mState != kStateDisabled) { @@ -501,6 +506,95 @@ Error TcatAgent::HandleStartThreadInterface(void) return error; } +void SeralizeTcatAdvertisementTlv(uint8_t *aBuffer, + uint16_t &aOffset, + TcatAdvertisementTlvType aType, + uint16_t aLength, + const uint8_t *aValue) +{ + aBuffer[aOffset++] = (uint8_t)(aType << 4 | (aLength & 0xf)); + memcpy(aBuffer + aOffset, aValue, aLength); + aOffset += aLength; +} + +uint8_t *TcatAgent::GetAdvertisementData(uint16_t &aLen) +{ + uint8_t *ret = nullptr; + DeviceTypeAndStatus tas; + otBleLinkCapabilities caps; + + VerifyOrExit(mVendorInfo != nullptr); + + aLen = 0; + ret = mAdvertisement; + + LittleEndian::WriteUint16(OT_TOBLE_SERVICE_UUID, mAdvertisement); + aLen += sizeof(uint16_t); + mAdvertisement[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE; + aLen++; + + if (mVendorInfo->mDeviceIds != nullptr) + { + for (uint8_t i = 0; mVendorInfo->mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) + { + switch (MapEnum(mVendorInfo->mDeviceIds[i].mDeviceIdType)) + { + case kTcatDeviceIdOui24: + SeralizeTcatAdvertisementTlv(mAdvertisement, aLen, kTlvVendorOui24, + mVendorInfo->mDeviceIds[i].mDeviceIdLen, + mVendorInfo->mDeviceIds[i].mDeviceId); + break; + case kTcatDeviceIdOui36: + SeralizeTcatAdvertisementTlv(mAdvertisement, aLen, kTlvVendorOui36, + mVendorInfo->mDeviceIds[i].mDeviceIdLen, + mVendorInfo->mDeviceIds[i].mDeviceId); + break; + case kTcatDeviceIdDiscriminator: + SeralizeTcatAdvertisementTlv(mAdvertisement, aLen, kTlvDeviceDiscriminator, + mVendorInfo->mDeviceIds[i].mDeviceIdLen, + mVendorInfo->mDeviceIds[i].mDeviceId); + break; + case kTcatDeviceIdIanaPen: + SeralizeTcatAdvertisementTlv(mAdvertisement, aLen, kTlvVendorIanaPen, + mVendorInfo->mDeviceIds[i].mDeviceIdLen, + mVendorInfo->mDeviceIds[i].mDeviceId); + break; + default: + break; + } + } + } + + otPlatBleGetLinkCapabilities(&GetInstance(), &caps); + + if (caps.mGattNotifications || caps.mL2CapDirect) + { + SeralizeTcatAdvertisementTlv(mAdvertisement, aLen, kTlvBleLinkCapabilities, kTlvBleLinkCapabilitiesLength, + reinterpret_cast(&caps)); + } + + tas.mRsv = 0; + tas.mMultiradioSupport = otPlatBleSupportsMultiRadio(&GetInstance()); + tas.mIsCommisionned = Get().IsCommissioned(); + tas.mThreadNetworkActive = Get().IsAttached(); + tas.mDeviceType = Get().GetDeviceMode().IsFullThreadDevice(); + tas.mRxOnWhenIdle = Get().GetDeviceMode().IsRxOnWhenIdle(); + +#if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE || \ + OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE) + tas.mIsBorderRouter = true; +#else + tas.mIsBorderRouter = false; +#endif + + SeralizeTcatAdvertisementTlv(mAdvertisement, aLen, kTlvDeviceTypeAndStatus, kTlvDeviceTypeAndStatusLength, + reinterpret_cast(&tas)); + OT_ASSERT(mAdvertisementLength <= OT_TCAT_ADVERTISEMENT_MAX_LEN); + +exit: + return ret; +} + } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index 16cf8f977438..ba049d8c9a73 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -154,10 +154,10 @@ class TcatAgent : public InstanceLocator, private NonCopyable }; /** - * TCAT TLV Types. + * TCAT Command TLV Types. * */ - enum TlvType : uint8_t + enum CommandTlvType : uint8_t { // Command Class General kTlvResponseWithStatus = 1, ///< TCAT response with status value TLV @@ -256,6 +256,19 @@ class TcatAgent : public InstanceLocator, private NonCopyable kStateConnected, }; + /** + * Represents Device ID type. + * + */ + enum TcatDeviceIdType : uint8_t + { + kTcatDeviceIdEmpty = OT_TCAT_DEVICE_ID_EMPTY, + kTcatDeviceIdOui24 = OT_TCAT_DEVICE_ID_OUI24, + kTcatDeviceIdOui36 = OT_TCAT_DEVICE_ID_OUI36, + kTcatDeviceIdDiscriminator = OT_TCAT_DEVICE_ID_DISCRIMINATOR, + kTcatDeviceIdIanaPen = OT_TCAT_DEVICE_ID_IANAPEN, + }; + /** * Initializes the Joiner object. * @@ -267,8 +280,6 @@ class TcatAgent : public InstanceLocator, private NonCopyable /** * Enables the TCAT protocol. * - * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method - * call, may be NULL). * @param[in] aAppDataReceiveCallback A pointer to a function that is called when the user data is received. * @param[in] aHandler A pointer to a function that is called when the join operation completes. * @param[in] aContext A context pointer. @@ -277,10 +288,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable * @retval kErrorInvalidArgs The aVendorInfo is invalid. * */ - Error Start(const VendorInfo &aVendorInfo, - AppDataReceiveCallback aAppDataReceiveCallback, - JoinCallback aHandler, - void *aContext); + Error Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext); /** * Stops the TCAT protocol. @@ -288,6 +296,14 @@ class TcatAgent : public InstanceLocator, private NonCopyable */ void Stop(void); + /** + * Set the TCAT Vendor Info object + * + * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method call). + * + */ + Error SetTcatVendorInfo(const VendorInfo &aVendorInfo); + /** * Indicates whether or not the TCAT agent is enabled. * @@ -317,6 +333,16 @@ class TcatAgent : public InstanceLocator, private NonCopyable */ bool IsCommandClassAuthorized(CommandClass aCommandClass) const; + /** + * Gets TCAT advertisement data. + * + * @param[out] aLen Advertisement length. + * + * @retval Pointer to buffer containing formatted data, or nullptr on error. + * + */ + uint8_t *GetAdvertisementData(uint16_t &aLen); + private: Error Connected(MeshCoP::SecureTransport &aTlsContext); void Disconnected(void); @@ -345,7 +371,8 @@ class TcatAgent : public InstanceLocator, private NonCopyable ExtendedPanId mCommissionerExtendedPanId; char mCurrentServiceName[OT_TCAT_MAX_SERVICE_NAME_LENGTH + 1]; State mState; - bool mAlreadyCommissioned : 1; + uint8_t mAdvertisement[OT_TCAT_ADVERTISEMENT_MAX_LEN]; + uint8_t mAdvertisementLength; bool mCommissionerHasNetworkName : 1; bool mCommissionerHasDomainName : 1; bool mCommissionerHasExtendedPanId : 1; @@ -358,9 +385,44 @@ class TcatAgent : public InstanceLocator, private NonCopyable DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo); DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol); +DefineMapEnum(otTcatDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType); +// Command class TLVs typedef UintTlvInfo ResponseWithStatusTlv; +/** + * Represent Device Type and Status + * + */ +struct DeviceTypeAndStatus +{ + uint8_t mRsv : 1; + bool mMultiradioSupport : 1; + bool mStoresActiveOpertonalDataset : 1; + bool mIsCommisionned : 1; + bool mThreadNetworkActive : 1; + bool mIsBorderRouter : 1; + bool mRxOnWhenIdle : 1; + bool mDeviceType : 1; +}; + +static constexpr uint8_t kTlvVendorOui24Length = 3; +static constexpr uint8_t kTlvVendorOui36Length = 5; +static constexpr uint8_t kTlvDeviceDiscriminatorLength = 5; +static constexpr uint8_t kTlvBleLinkCapabilitiesLength = 1; +static constexpr uint8_t kTlvDeviceTypeAndStatusLength = 1; +static constexpr uint8_t kTlvVendorIanaPenLength = 4; + +enum TcatAdvertisementTlvType : uint8_t +{ + kTlvVendorOui24 = 1, ///< TCAT vendor OUI 24 + kTlvVendorOui36 = 2, ///< TCAT vendor OUI 36 + kTlvDeviceDiscriminator = 3, ///< TCAT random vendor discriminator + kTlvDeviceTypeAndStatus = 4, ///< TCAT Thread device type and status + kTlvBleLinkCapabilities = 5, ///< TCAT BLE link capabilities of device + kTlvVendorIanaPen = 6, ///< TCAT Vendor IANA PEN +}; + } // namespace ot #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp index 8f4a0a94c094..3f4bae96ae4e 100644 --- a/src/core/radio/ble_secure.cpp +++ b/src/core/radio/ble_secure.cpp @@ -64,7 +64,9 @@ BleSecure::BleSecure(Instance &aInstance) Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext) { - Error error = kErrorNone; + Error error = kErrorNone; + uint16_t advertisementLen = 0; + uint8_t *advertisementData = nullptr; VerifyOrExit(mBleState == kStopped, error = kErrorAlready); @@ -74,7 +76,12 @@ Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceive mMtuSize = kInitialMtuSize; SuccessOrExit(error = otPlatBleEnable(&GetInstance())); + + advertisementData = mTcatAgent.GetAdvertisementData(advertisementLen); + VerifyOrExit(advertisementData != nullptr, error = kErrorFailed); + SuccessOrExit(error = otPlatBleGapAdvSetData(&GetInstance(), advertisementData, advertisementLen)); SuccessOrExit(error = otPlatBleGapAdvStart(&GetInstance(), OT_BLE_ADV_INTERVAL_DEFAULT)); + SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnected, this)); SuccessOrExit(error = mTls.Bind(HandleTransport, this)); @@ -86,14 +93,13 @@ Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceive return error; } -Error BleSecure::TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo, - MeshCoP::TcatAgent::JoinCallback aJoinHandler) +Error BleSecure::TcatStart(MeshCoP::TcatAgent::JoinCallback aJoinHandler) { Error error; VerifyOrExit(mBleState != kStopped, error = kErrorInvalidState); - error = mTcatAgent.Start(aVendorInfo, mReceiveCallback.GetHandler(), aJoinHandler, mReceiveCallback.GetContext()); + error = mTcatAgent.Start(mReceiveCallback.GetHandler(), aJoinHandler, mReceiveCallback.GetContext()); exit: return error; diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp index 63a86aacdc51..84c9aa9ee4c1 100644 --- a/src/core/radio/ble_secure.hpp +++ b/src/core/radio/ble_secure.hpp @@ -101,7 +101,6 @@ class BleSecure : public InstanceLocator, private NonCopyable /** * Enables the TCAT protocol over BLE Secure. * - * @param[in] aVendorInfo A reference to the Vendor Information (must remain valid after the method call) * @param[in] aHandler Callback to a function that is called when the join operation completes. * * @retval kErrorNone Successfully started the BLE Secure Joiner role. @@ -109,7 +108,18 @@ class BleSecure : public InstanceLocator, private NonCopyable * @retval kErrorInvaidState The BLE function has not been started or line mode is not selected. * */ - Error TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo, MeshCoP::TcatAgent::JoinCallback aHandler); + Error TcatStart(MeshCoP::TcatAgent::JoinCallback aHandler); + + /** + * Set the TCAT Vendor Info object + * + * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method call). + * + */ + Error TcatSetVendorInfo(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo) + { + return mTcatAgent.SetTcatVendorInfo(aVendorInfo); + } /** * Stops the secure BLE agent. diff --git a/src/posix/platform/ble.cpp b/src/posix/platform/ble.cpp index 2fe3c64535d5..307489497add 100644 --- a/src/posix/platform/ble.cpp +++ b/src/posix/platform/ble.cpp @@ -73,3 +73,26 @@ otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, con OT_UNUSED_VARIABLE(aPacket); return OT_ERROR_NOT_IMPLEMENTED; } + +void otPlatBleGetLinkCapabilities(otInstance *aInstance, otBleLinkCapabilities *aBleLinkCapabilities) +{ + OT_UNUSED_VARIABLE(aInstance); + + aBleLinkCapabilities->mGattNotifications = 1; + aBleLinkCapabilities->mL2CapDirect = 0; + aBleLinkCapabilities->mRsv = 0; +} + +otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aAdvertisementData); + OT_UNUSED_VARIABLE(aAdvertisementLen); + return OT_ERROR_NONE; +} + +bool otPlatBleSupportsMultiRadio(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return false; +} diff --git a/tests/scripts/expect/cli-tcat-advertisement.exp b/tests/scripts/expect/cli-tcat-advertisement.exp new file mode 100755 index 000000000000..37628459dea5 --- /dev/null +++ b/tests/scripts/expect/cli-tcat-advertisement.exp @@ -0,0 +1,75 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2022, 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. +# + +source "tests/scripts/expect/_common.exp" + +spawn_node 1 "cli" + +switch_node 1 +send "tcat vendorid ianapen f378\n" +expect_line "Done" + +send "tcat vendorid\n" +expect_line "type ianapen, value: f378" +expect_line "Done" + +send "tcat vendorid ianapen f378aabb\n" +expect_line "Done" + +send "tcat vendorid\n" +expect_line "type ianapen, value: f378aabb" +expect_line "Done" + +send "tcat vendorid oui24 f378aa\n" +expect_line "Done" + +send "tcat vendorid\n" +expect_line "type oui24, value: f378aa" +expect_line "Done" + +send "tcat vendorid oui36 f378aabbcc\n" +expect_line "Done" + +send "tcat vendorid\n" +expect_line "type oui36, value: f378aabbcc" +expect_line "Done" + +send "tcat vendorid discriminator f378aabbdd\n" +expect_line "Done" + +send "tcat vendorid\n" +expect_line "type discriminator, value: f378aabbdd" +expect_line "Done" + +send "tcat vendorid empty\n" +expect_line "Done" + +send "tcat vendorid\n" +expect_line "empty" +expect_line "Done" diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index c30c68ebc2c7..86c7c9b41030 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -765,6 +765,30 @@ otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, con OT_UNUSED_VARIABLE(aPacket); return OT_ERROR_NONE; } + +void otPlatBleGetLinkCapabilities(otInstance *aInstance, otBleLinkCapabilities *aBleLinkCapabilities) +{ + OT_UNUSED_VARIABLE(aInstance); + + aBleLinkCapabilities->mGattNotifications = true; + aBleLinkCapabilities->mL2CapDirect = false; + aBleLinkCapabilities->mRsv = 0; +} + +bool otPlatBleSupportsMultiRadio(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return false; +} + +otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aAdvertisementData); + OT_UNUSED_VARIABLE(aAdvertisementLen); + return OT_ERROR_NONE; +} + #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE #if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE diff --git a/tests/unit/test_tcat.cpp b/tests/unit/test_tcat.cpp index 6767a0f49f71..75afb9ab72b1 100644 --- a/tests/unit/test_tcat.cpp +++ b/tests/unit/test_tcat.cpp @@ -122,10 +122,11 @@ void TestTcat(void) otBleSecureSetSslAuthMode(instance, true); // Validate BLE secure and Tcat start APIs - VerifyOrQuit(otBleSecureTcatStart(instance, &vendorInfo, nullptr) == kErrorInvalidState); + SuccessOrQuit(otBleSecureSetTcatVendorInfo(instance, &vendorInfo)); + VerifyOrQuit(otBleSecureTcatStart(instance, nullptr) == kErrorInvalidState); SuccessOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, &ble)); VerifyOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, nullptr) == kErrorAlready); - SuccessOrQuit(otBleSecureTcatStart(instance, &vendorInfo, nullptr)); + SuccessOrQuit(otBleSecureTcatStart(instance, nullptr)); // Validate connection callbacks when platform informs that peer has connected/disconnected otPlatBleGapOnConnected(instance, kConnectionId);