From 4b42cd07d9a5e8b834c13cdb8c1db4123ca0d77d Mon Sep 17 00:00:00 2001 From: Przemyslaw Bida Date: Fri, 19 Jul 2024 13:32:37 +0200 Subject: [PATCH] [tcat] Implement new tcat General commands. New General TLV's implemented: - Get network name - Get device id - Get ext pan ID - get provisioning URL --- include/openthread/instance.h | 2 +- include/openthread/tcat.h | 55 ++++--- src/cli/README_TCAT.md | 87 +++++++++++ src/cli/cli_tcat.cpp | 91 +++++++++-- src/core/meshcop/tcat_agent.cpp | 130 +++++++++++++--- src/core/meshcop/tcat_agent.hpp | 19 ++- .../scripts/expect/cli-tcat-advertisement.exp | 44 ++++-- tests/scripts/expect/cli-tcat.exp | 25 ++++ tools/tcat_ble_client/cli/base_commands.py | 141 ++++++++++++------ tools/tcat_ble_client/cli/cli.py | 7 +- tools/tcat_ble_client/cli/command.py | 3 +- tools/tcat_ble_client/tlv/tcat_tlv.py | 4 + 12 files changed, 488 insertions(+), 120 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index f5c12bbf1c6c..891398807bea 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 (433) +#define OPENTHREAD_API_VERSION (434) /** * @addtogroup api-instance diff --git a/include/openthread/tcat.h b/include/openthread/tcat.h index d1b9c8c45e30..1733d9fb580c 100644 --- a/include/openthread/tcat.h +++ b/include/openthread/tcat.h @@ -67,9 +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. +#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_ADVERTISED_DEVICEID_SIZE 5 ///< TCAT max size of any type of advertised Device ID. +#define OT_TCAT_MAX_DEVICEID_SIZE 64 ///< TCAT max size of device ID. /** * Represents TCAT status code. @@ -116,10 +117,10 @@ typedef enum otTcatCommandClass } otTcatCommandClass; /** - * Represents Device ID type. + * Represents Advertised Device ID type. (used during TCAT advertisement) * */ -typedef enum otTcatDeviceIdType +typedef enum otTcatAdvertisedDeviceIdType { 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 @@ -127,14 +128,24 @@ typedef enum otTcatDeviceIdType 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; +} otTcatAdvertisedDeviceIdType; -typedef struct otTcatDeviceId +typedef struct otTcatAdvertisedDeviceId { - otTcatDeviceIdType mDeviceIdType; - uint16_t mDeviceIdLen; - uint8_t mDeviceId[OT_TCAT_MAX_VENDORID_SIZE]; -} otTcatDeviceId; + otTcatAdvertisedDeviceIdType mDeviceIdType; + uint16_t mDeviceIdLen; + uint8_t mDeviceId[OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE]; +} otTcatAdvertisedDeviceId; + +/** + * Represents General Device ID type. + * + */ +typedef struct otTcatGeneralDeviceId +{ + uint16_t mDeviceIdLen; + uint8_t mDeviceId[OT_TCAT_MAX_DEVICEID_SIZE]; +} otTcatGeneralDeviceId; /** * This structure represents a TCAT vendor information. @@ -144,16 +155,18 @@ typedef struct otTcatDeviceId */ 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 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 */ + 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 otTcatAdvertisedDeviceId *mAdvertisedDeviceIds; /** Vendor managed advertised device ID array. + Array is terminated like C string with OT_TCAT_DEVICE_ID_EMPTY */ + const otTcatGeneralDeviceId *mGeneralDeviceId; /** Vendor managed general device ID array. + (if NULL: device ID is set to EUI-64 in binary format)*/ + } otTcatVendorInfo; /** diff --git a/src/cli/README_TCAT.md b/src/cli/README_TCAT.md index 066a57109827..37cb4d28617b 100644 --- a/src/cli/README_TCAT.md +++ b/src/cli/README_TCAT.md @@ -2,16 +2,103 @@ ## Command List +- advid [#advid] +- devid [#devid] - help [#help] - start [#start] - stop [#stop] +### advid + +Displays currently set TCAT advertised ids. + +```bash +tcat advid +type oui24, value: f378aa +Done +``` + +### advid ianapen \ + +Sets TCAT advertised ianapen id. + +```bash +tcat advid ianapen f378aabb +Done +``` + +### advid oui24 \ + +Sets TCAT advertised oui24 id. + +```bash +tcat advid oui24 f378aa +Done +``` + +### advid oui36 \ + +Sets TCAT advertised oui36 id. + +```bash +tcat advid oui36 f378aabbcc +Done +``` + +### advid discriminator \ + +Sets TCAT advertised discriminator id. + +```bash +tcat advid discriminator f378aabbdd +Done +``` + +### advid empty + +Clears TCAT advertised id. + +```bash +tcat advid empty +Done +``` + +### devid + +Displays currently set TCAT device id. + +```bash +tcat devid +abcd +Done +``` + +### devid \ + +Sets TCAT device id. + +```bash +tcat devid abcd +Done +``` + +### devid empty + +Clears TCAT device id. + +```bash +tcat devid empty +Done +``` + ### help print help ```bash tcat help +advid +devid help start stop diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp index ab29bd0fdeef..a76b289b87c7 100644 --- a/src/cli/cli_tcat.cpp +++ b/src/cli/cli_tcat.cpp @@ -32,6 +32,7 @@ #include "cli/cli_tcat.hpp" #include "common/code_utils.hpp" +#include "common/error.hpp" #include @@ -88,11 +89,26 @@ namespace ot { namespace Cli { -otTcatDeviceId sVendorDeviceIds[OT_TCAT_DEVICE_ID_MAX]; +otTcatAdvertisedDeviceId sAdvertisedDeviceIds[OT_TCAT_DEVICE_ID_MAX]; +otTcatGeneralDeviceId sGeneralDeviceId; const char kPskdVendor[] = "JJJJJJ"; const char kUrl[] = "dummy_url"; +static bool IsDeviceIdSet(void) +{ + bool ret = false; + for (const otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds) + { + if (vendorDeviceId.mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) + { + ExitNow(ret = true); + } + } +exit: + return ret; +} + static void HandleBleSecureReceive(otInstance *aInstance, const otMessage *aMessage, int32_t aOffset, @@ -121,24 +137,24 @@ static void HandleBleSecureReceive(otInstance *aInstance, IgnoreReturnValue(otBleSecureFlush(aInstance)); } -template <> otError Tcat::Process(Arg aArgs[]) +template <> otError Tcat::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - otTcatDeviceId devId; + otTcatAdvertisedDeviceId devId; static const char *const kVendorIdTypes[] = {"empty", "oui24", "oui36", "discriminator", "ianapen"}; - mVendorInfo.mDeviceIds = sVendorDeviceIds; + mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds; if (aArgs[0].IsEmpty()) { - if (mVendorInfo.mDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY) + if (mVendorInfo.mAdvertisedDeviceIds[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++) + OutputLine("Set advertisedIds:"); + for (size_t i = 0; mVendorInfo.mAdvertisedDeviceIds[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); + OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType]); + OutputBytesLine(const_cast(mVendorInfo.mAdvertisedDeviceIds[i].mDeviceId), + mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdLen); } } else @@ -166,7 +182,7 @@ template <> otError Tcat::Process(Arg aArgs[]) } else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY]) { - for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds) { vendorDeviceId.mDeviceIdType = OT_TCAT_DEVICE_ID_EMPTY; vendorDeviceId.mDeviceIdLen = 0; @@ -178,11 +194,11 @@ template <> otError Tcat::Process(Arg aArgs[]) ExitNow(error = OT_ERROR_INVALID_ARGS); } - if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_VENDORID_SIZE * 2 + 1)) + if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE * 2 + 1)) { - devId.mDeviceIdLen = OT_TCAT_MAX_VENDORID_SIZE; + devId.mDeviceIdLen = OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE; SuccessOrExit(error = aArgs[1].ParseAsHexString(devId.mDeviceIdLen, devId.mDeviceId)); - for (otTcatDeviceId &vendorDeviceId : sVendorDeviceIds) + for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds) { if (vendorDeviceId.mDeviceIdType == devId.mDeviceIdType || vendorDeviceId.mDeviceIdType == OT_TCAT_DEVICE_ID_EMPTY) @@ -200,6 +216,39 @@ template <> otError Tcat::Process(Arg aArgs[]) return error; } +template <> otError Tcat::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0].IsEmpty()) + { + OutputLine("TCAT GeneralId:"); + if (sGeneralDeviceId.mDeviceIdLen != 0) + { + OutputBytesLine(sGeneralDeviceId.mDeviceId, sGeneralDeviceId.mDeviceIdLen); + } + else + { + OutputLine("empty"); + } + ExitNow(); + } + + if (aArgs[0] == "empty") + { + ClearAllBytes(sGeneralDeviceId); + } + else + { + VerifyOrExit(aArgs[0].GetLength() < (OT_TCAT_MAX_DEVICEID_SIZE * 2 + 1), error = OT_ERROR_INVALID_ARGS); + sGeneralDeviceId.mDeviceIdLen = OT_TCAT_MAX_DEVICEID_SIZE; + SuccessOrExit(error = aArgs[0].ParseAsHexString(sGeneralDeviceId.mDeviceIdLen, sGeneralDeviceId.mDeviceId)); + } + +exit: + return error; +} + template <> otError Tcat::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -210,6 +259,16 @@ template <> otError Tcat::Process(Arg aArgs[]) mVendorInfo.mPskdString = kPskdVendor; mVendorInfo.mProvisioningUrl = kUrl; + if (IsDeviceIdSet()) + { + mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds; + } + + if (sGeneralDeviceId.mDeviceIdLen != 0) + { + mVendorInfo.mGeneralDeviceId = &sGeneralDeviceId; + } + otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast(OT_CLI_TCAT_X509_CERT), sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast(OT_CLI_TCAT_PRIV_KEY), sizeof(OT_CLI_TCAT_PRIV_KEY)); @@ -244,10 +303,12 @@ otError Tcat::Process(Arg aArgs[]) aCommandString, &Tcat::Process \ } - static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop"), CmdEntry("vendorid")}; + static constexpr Command kCommands[] = {CmdEntry("advid"), CmdEntry("devid"), CmdEntry("start"), CmdEntry("stop")}; static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); +#undef CmdEntry + otError error = OT_ERROR_NONE; const Command *command; diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 49470828dc07..8f2c7c2e73c0 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -33,6 +33,7 @@ #include "tcat_agent.hpp" #include +#include "meshcop/network_name.hpp" #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE @@ -55,7 +56,11 @@ RegisterLogModule("TcatAgent"); bool TcatAgent::VendorInfo::IsValid(void) const { - return mProvisioningUrl == nullptr || IsValidUtf8String(mProvisioningUrl) || mPskdString != nullptr; + return (mProvisioningUrl == nullptr || + (IsValidUtf8String(mProvisioningUrl) && + (static_cast(StringLength(mProvisioningUrl, kProvisioningUrlMaxLength)) < + kProvisioningUrlMaxLength))) && + mPskdString != nullptr; } TcatAgent::TcatAgent(Instance &aInstance) @@ -414,17 +419,24 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncomingMessage, Message &aOutg error = HandleDecomission(); break; case kTlvPing: - error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length); - if (error == kErrorNone) - { - response = true; - } + error = HandlePing(aIncomingMessage, aOutgoingMessage, offset, length, response); + break; + case kTlvGetNetworkName: + error = HandleGetNetworkName(aOutgoingMessage, response); + break; + case kTlvGetDeviceId: + error = HandleGetDeviceId(aOutgoingMessage, response); + break; + case kTlvGetExtendedPanID: + error = HandleGetExtPanId(aOutgoingMessage, response); + break; + case kTlvGetProvisioningURL: + error = HandleGetProvisioningUrl(aOutgoingMessage, response); break; default: error = kErrorInvalidCommand; } } - if (!response) { StatusCode statusCode; @@ -514,7 +526,8 @@ Error TcatAgent::HandleDecomission(void) Error TcatAgent::HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, - uint16_t aLength) + uint16_t aLength, + bool &response) { Error error = kErrorNone; ot::ExtendedTlv extTlv; @@ -535,6 +548,85 @@ Error TcatAgent::HandlePing(const Message &aIncomingMessage, } SuccessOrExit(error = aOutgoingMessage.AppendBytesFromMessage(aIncomingMessage, aOffset, aLength)); + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetNetworkName(Message &aOutgoingMessage, bool &response) +{ + Error error = kErrorNone; + MeshCoP::NameData nameData = Get().GetNetworkName().GetAsData(); + + VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); +#if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME + VerifyOrExit(nameData.GetLength() > 0, error = kErrorInvalidState); +#endif + + SuccessOrExit( + error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, nameData.GetBuffer(), nameData.GetLength())); + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetDeviceId(Message &aOutgoingMessage, bool &response) +{ + const uint8_t *deviceId; + uint16_t length = 0; + Mac::ExtAddress eui64; + Error error = kErrorNone; + + if (mVendorInfo->mGeneralDeviceId != nullptr) + { + length = mVendorInfo->mGeneralDeviceId->mDeviceIdLen; + deviceId = mVendorInfo->mGeneralDeviceId->mDeviceId; + } + + if (length == 0) + { + Get().GetIeeeEui64(eui64); + + length = sizeof(Mac::ExtAddress); + deviceId = eui64.m8; + } + + SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, deviceId, length)); + + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetExtPanId(Message &aOutgoingMessage, bool &response) +{ + Error error; + + VerifyOrExit(Get().IsCommissioned(), error = kErrorInvalidState); + + SuccessOrExit(error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, + &Get().GetExtPanId(), sizeof(ExtendedPanId))); + response = true; + +exit: + return error; +} + +Error TcatAgent::HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response) +{ + Error error = kErrorNone; + uint16_t length; + + VerifyOrExit(mVendorInfo->mProvisioningUrl != nullptr, error = kErrorInvalidState); + + length = StringLength(mVendorInfo->mProvisioningUrl, kProvisioningUrlMaxLength); + VerifyOrExit(length > 0 && length <= Tlv::kBaseTlvMaxLength, error = kErrorInvalidState); + + error = Tlv::AppendTlv(aOutgoingMessage, kTlvResponseWithPayload, mVendorInfo->mProvisioningUrl, length); + response = true; exit: return error; @@ -585,31 +677,31 @@ Error TcatAgent::GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementDat aAdvertisementData[2] = OPENTHREAD_CONFIG_THREAD_VERSION << 4 | OT_TCAT_OPCODE; aLen++; - if (mVendorInfo->mDeviceIds != nullptr) + if (mVendorInfo->mAdvertisedDeviceIds != nullptr) { - for (uint8_t i = 0; mVendorInfo->mDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) + for (uint8_t i = 0; mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++) { - switch (MapEnum(mVendorInfo->mDeviceIds[i].mDeviceIdType)) + switch (MapEnum(mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdType)) { case kTcatDeviceIdOui24: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui24, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; case kTcatDeviceIdOui36: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorOui36, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; case kTcatDeviceIdDiscriminator: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvDeviceDiscriminator, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; case kTcatDeviceIdIanaPen: SeralizeTcatAdvertisementTlv(aAdvertisementData, aLen, kTlvVendorIanaPen, - mVendorInfo->mDeviceIds[i].mDeviceIdLen, - mVendorInfo->mDeviceIds[i].mDeviceId); + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceIdLen, + mVendorInfo->mAdvertisedDeviceIds[i].mDeviceId); break; default: break; diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp index 5d6d0dd9797e..a76008513c74 100644 --- a/src/core/meshcop/tcat_agent.hpp +++ b/src/core/meshcop/tcat_agent.hpp @@ -168,6 +168,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable kTlvPing = 10, ///< TCAT ping request TLV kTlvGetDeviceId = 11, ///< TCAT device ID query TLV kTlvGetExtendedPanID = 12, ///< TCAT extended PAN ID query TLV + kTlvGetProvisioningURL = 13, ///< TCAT provisioning URL query TLV kTlvPresentPskdHash = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash kTlvPresentPskcHash = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash kTlvPresentInstallCodeHash = 18, ///< TCAT commissioner rights elevation request TLV using install code @@ -353,7 +354,15 @@ class TcatAgent : public InstanceLocator, private NonCopyable Error HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage); Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength); Error HandleDecomission(void); - Error HandlePing(const Message &aIncomingMessage, Message &aOutgoingMessage, uint16_t aOffset, uint16_t aLength); + Error HandlePing(const Message &aIncomingMessage, + Message &aOutgoingMessage, + uint16_t aOffset, + uint16_t aLength, + bool &response); + Error HandleGetNetworkName(Message &aOutgoingMessage, bool &response); + Error HandleGetDeviceId(Message &aOutgoingMessage, bool &response); + Error HandleGetExtPanId(Message &aOutgoingMessage, bool &response); + Error HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response); Error HandleStartThreadInterface(void); bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, @@ -362,8 +371,10 @@ class TcatAgent : public InstanceLocator, private NonCopyable bool CanProcessTlv(uint8_t aTlvType) const; CommandClass GetCommandClass(uint8_t aTlvType) const; - static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; - static constexpr uint16_t kPingPayloadMaxLength = 512; + static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + static constexpr uint16_t kPingPayloadMaxLength = 512; + static constexpr uint16_t kProvisioningUrlMaxLength = 64; + static constexpr uint16_t kTcatMaxDeviceIdSize = OT_TCAT_MAX_DEVICEID_SIZE; JoinerPskd mJoinerPskd; const VendorInfo *mVendorInfo; @@ -389,7 +400,7 @@ class TcatAgent : public InstanceLocator, private NonCopyable DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo); DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol); -DefineMapEnum(otTcatDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType); +DefineMapEnum(otTcatAdvertisedDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType); // Command class TLVs typedef UintTlvInfo ResponseWithStatusTlv; diff --git a/tests/scripts/expect/cli-tcat-advertisement.exp b/tests/scripts/expect/cli-tcat-advertisement.exp index 37628459dea5..724f5cc759e1 100755 --- a/tests/scripts/expect/cli-tcat-advertisement.exp +++ b/tests/scripts/expect/cli-tcat-advertisement.exp @@ -32,44 +32,64 @@ source "tests/scripts/expect/_common.exp" spawn_node 1 "cli" switch_node 1 -send "tcat vendorid ianapen f378\n" +send "tcat advid ianapen f378\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type ianapen, value: f378" expect_line "Done" -send "tcat vendorid ianapen f378aabb\n" +send "tcat advid ianapen f378aabb\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type ianapen, value: f378aabb" expect_line "Done" -send "tcat vendorid oui24 f378aa\n" +send "tcat advid oui24 f378aa\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type oui24, value: f378aa" expect_line "Done" -send "tcat vendorid oui36 f378aabbcc\n" +send "tcat advid oui36 f378aabbcc\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type oui36, value: f378aabbcc" expect_line "Done" -send "tcat vendorid discriminator f378aabbdd\n" +send "tcat advid discriminator f378aabbdd\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "type discriminator, value: f378aabbdd" expect_line "Done" -send "tcat vendorid empty\n" +send "tcat advid empty\n" expect_line "Done" -send "tcat vendorid\n" +send "tcat advid\n" expect_line "empty" expect_line "Done" + +send "tcat devid\n" +expect_line "empty" +expect_line "Done" + +send "tcat devid aaaa\n" +expect_line "aaaa" +expect_line "Done" + +send "tcat devid\n" +expect_line "aaaa" +expect_line "Done" + +send "tcat devid empty\n" +expect_line "empty" +expect_line "Done" + +send "tcat devid\n" +expect_line "empty" +expect_line "Done" \ No newline at end of file diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp index 9617cb39a3a6..53d344b89847 100755 --- a/tests/scripts/expect/cli-tcat.exp +++ b/tests/scripts/expect/cli-tcat.exp @@ -38,6 +38,11 @@ expect_line "Done" spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth" set py_client "$spawn_id" expect_line "Done" + +send "network_name\n" +expect_line "\tTYPE:\tRESPONSE_W_STATUS" +expect_line "\tVALUE:\t0x06" + send "commission\n" expect_line "\tTYPE:\tRESPONSE_W_STATUS" expect_line "\tVALUE:\t0x00" @@ -58,6 +63,26 @@ send "ping 512\n" expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" expect_line "\tLEN:\t512" +send "network_name\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t15" +expect_line "\tVALUE:\t0x4f70656e5468726561642d63363465" + +send "device_id\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0x18b4300000000001" + +send "ext_panid\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t8" +expect_line "\tVALUE:\t0xef1398c2fd504b67" + +send "provisioning_url\n" +expect_line "\tTYPE:\tRESPONSE_W_PAYLOAD" +expect_line "\tLEN:\t9" +expect_line "\tVALUE:\t0x64756d6d795f75726c" + send "exit\n" expect eof diff --git a/tools/tcat_ble_client/cli/base_commands.py b/tools/tcat_ble_client/cli/base_commands.py index bb0258ee27e8..411bc821e932 100644 --- a/tools/tcat_ble_client/cli/base_commands.py +++ b/tools/tcat_ble_client/cli/base_commands.py @@ -26,6 +26,7 @@ POSSIBILITY OF SUCH DAMAGE. """ +from abc import abstractmethod from ble.ble_connection_constants import BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, \ BBTC_RX_CHAR_UUID from ble.ble_stream import BleStream @@ -54,15 +55,21 @@ async def execute_default(self, args, context): return CommandResultNone() -class HelloCommand(Command): +class BleCommand(Command): - def get_help_string(self) -> str: - return 'Send round trip "Hello world!" message.' + @abstractmethod + def get_log_string(self) -> str: + pass + + @abstractmethod + def prepare_data(self, context): + pass async def execute_default(self, args, context): bless: BleStreamSecure = context['ble_sstream'] - print('Sending hello world...') - data = TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes() + + print(self.get_log_string()) + data = self.prepare_data(context) response = await bless.send_with_resp(data) if not response: return @@ -70,39 +77,90 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) -class CommissionCommand(Command): +class HelloCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Sending hello world...' + + def get_help_string(self) -> str: + return 'Send round trip "Hello world!" message.' + + def prepare_data(self, context): + return TLV(TcatTLVType.APPLICATION.value, bytes('Hello world!', 'ascii')).to_bytes() + + +class CommissionCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Commissioning...' def get_help_string(self) -> str: return 'Update the connected device with current dataset.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] + def prepare_data(self, context): dataset: ThreadDataset = context['dataset'] - - print('Commissioning...') dataset_bytes = dataset.to_bytes() - data = TLV(TcatTLVType.ACTIVE_DATASET.value, dataset_bytes).to_bytes() - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) + return TLV(TcatTLVType.ACTIVE_DATASET.value, dataset_bytes).to_bytes() -class DecommissionCommand(Command): +class DecommissionCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Disabling Thread and decommissioning device...' def get_help_string(self) -> str: return 'Stop Thread interface and decommission device from current network.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] - print('Disabling Thread and decommissioning device...') - data = (TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes()) - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) + def prepare_data(self, context): + return TLV(TcatTLVType.DECOMMISSION.value, bytes()).to_bytes() + + +class GetDeviceIdCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving device id.' + + def get_help_string(self) -> str: + return 'Get unique identifier for the TCAT device.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_DEVICE_ID.value, bytes()).to_bytes() + + +class GetExtPanIDCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving extended PAN ID.' + + def get_help_string(self) -> str: + return 'Get extended PAN ID that is commissioned in the active dataset.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_EXT_PAN_ID.value, bytes()).to_bytes() + + +class GetProvisioningUrlCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving provisioning url.' + + def get_help_string(self) -> str: + return 'Get a URL for an application suited to commission the TCAT device.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_PROVISIONING_URL.value, bytes()).to_bytes() + + +class GetNetworkNameCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Retrieving network name.' + + def get_help_string(self) -> str: + return 'Get the Thread network name that is commissioned in the active dataset.' + + def prepare_data(self, context): + return TLV(TcatTLVType.GET_NETWORK_NAME.value, bytes()).to_bytes() class PingCommand(Command): @@ -136,37 +194,28 @@ async def execute_default(self, args, context): return CommandResultTLV(tlv_response) -class ThreadStartCommand(Command): +class ThreadStartCommand(BleCommand): + + def get_log_string(self) -> str: + return 'Enabling Thread...' def get_help_string(self) -> str: return 'Enable thread interface.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] + def prepare_data(self, context): + return TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes() - print('Enabling Thread...') - data = TLV(TcatTLVType.THREAD_START.value, bytes()).to_bytes() - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) +class ThreadStopCommand(BleCommand): -class ThreadStopCommand(Command): + def get_log_string(self) -> str: + return 'Disabling Thread...' def get_help_string(self) -> str: return 'Disable thread interface.' - async def execute_default(self, args, context): - bless: BleStreamSecure = context['ble_sstream'] - print('Disabling Thread...') - data = TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes() - response = await bless.send_with_resp(data) - if not response: - return - tlv_response = TLV.from_bytes(response) - return CommandResultTLV(tlv_response) + def prepare_data(self, context): + return TLV(TcatTLVType.THREAD_STOP.value, bytes()).to_bytes() class ThreadStateCommand(Command): diff --git a/tools/tcat_ble_client/cli/cli.py b/tools/tcat_ble_client/cli/cli.py index 28c7fb9a1763..01e33bac31a5 100644 --- a/tools/tcat_ble_client/cli/cli.py +++ b/tools/tcat_ble_client/cli/cli.py @@ -29,7 +29,8 @@ import readline import shlex from ble.ble_stream_secure import BleStreamSecure -from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, PingCommand, +from cli.base_commands import (HelpCommand, HelloCommand, CommissionCommand, DecommissionCommand, GetDeviceIdCommand, + GetExtPanIDCommand, GetNetworkNameCommand, GetProvisioningUrlCommand, PingCommand, ThreadStateCommand, ScanCommand) from cli.dataset_commands import (DatasetCommand) from dataset.dataset import ThreadDataset @@ -44,6 +45,10 @@ def __init__(self, dataset: ThreadDataset, ble_sstream: Optional[BleStreamSecure 'hello': HelloCommand(), 'commission': CommissionCommand(), 'decommission': DecommissionCommand(), + 'device_id': GetDeviceIdCommand(), + 'ext_panid': GetExtPanIDCommand(), + 'provisioning_url': GetProvisioningUrlCommand(), + 'network_name': GetNetworkNameCommand(), 'ping': PingCommand(), 'dataset': DatasetCommand(), 'thread': ThreadStateCommand(), diff --git a/tools/tcat_ble_client/cli/command.py b/tools/tcat_ble_client/cli/command.py index f494a3e7d675..e6b819b10ba7 100644 --- a/tools/tcat_ble_client/cli/command.py +++ b/tools/tcat_ble_client/cli/command.py @@ -28,6 +28,7 @@ from tlv.tlv import TLV from tlv.tcat_tlv import TcatTLVType +from ble.ble_stream_secure import BleStreamSecure from abc import ABC, abstractmethod @@ -57,7 +58,7 @@ async def execute_subcommand(self, args, context) -> CommandResult: return await self._subcommands[args[0]].execute(args[1:], context) @abstractmethod - async def execute_default(self, args, context) -> CommandResult: + async def execute_default(self, args, context): pass @abstractmethod diff --git a/tools/tcat_ble_client/tlv/tcat_tlv.py b/tools/tcat_ble_client/tlv/tcat_tlv.py index a276cafb2007..7d26864ae787 100644 --- a/tools/tcat_ble_client/tlv/tcat_tlv.py +++ b/tools/tcat_ble_client/tlv/tcat_tlv.py @@ -31,8 +31,12 @@ class TcatTLVType(Enum): RESPONSE_W_STATUS = 0x01 RESPONSE_W_PAYLOAD = 0x02 + GET_NETWORK_NAME = 0x08 DISCONNECT = 0x09 PING = 0x0A + GET_DEVICE_ID = 0x0B + GET_EXT_PAN_ID = 0x0C + GET_PROVISIONING_URL = 0x0D ACTIVE_DATASET = 0x20 DECOMMISSION = 0x60 APPLICATION = 0x82