From 28922925b7783a0cbca4d75f57eaf2bec79ae8b6 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Wed, 17 Jul 2024 00:01:49 -0700 Subject: [PATCH 01/14] Implement Commissioner Control cluster server --- .../fabric-bridge-app.matter | 66 +++++ .../fabric-bridge-app.zap | 158 +++++++++++ .../fabric-bridge-common/src/ZCLCallbacks.cpp | 11 + .../commissioner-control-server.cpp | 251 ++++++++++++++++++ .../commissioner-control-server.h | 99 +++++++ 5 files changed, 585 insertions(+) create mode 100644 src/app/clusters/commissioner-control-server/commissioner-control-server.cpp create mode 100644 src/app/clusters/commissioner-control-server/commissioner-control-server.h diff --git a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter index 7f3de425528f64..46bd51d578e6ee 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter +++ b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.matter @@ -1334,6 +1334,57 @@ cluster GroupKeyManagement = 63 { fabric command access(invoke: administer) KeySetReadAllIndices(): KeySetReadAllIndicesResponse = 4; } +/** Supports the ability for clients to request the commissioning of themselves or other nodes onto a fabric which the cluster server can commission onto. */ +provisional cluster CommissionerControl = 1873 { + revision 1; + + bitmap SupportedDeviceCategoryBitmap : bitmap32 { + kFabricSynchronization = 0x1; + } + + fabric_sensitive info event access(read: manage) CommissioningRequestResult = 0 { + int64u requestId = 0; + node_id clientNodeId = 1; + enum8 statusCode = 2; + fabric_idx fabricIndex = 254; + } + + readonly attribute access(read: manage) SupportedDeviceCategoryBitmap supportedDeviceCategories = 0; + readonly attribute command_id generatedCommandList[] = 65528; + readonly attribute command_id acceptedCommandList[] = 65529; + readonly attribute event_id eventList[] = 65530; + readonly attribute attrib_id attributeList[] = 65531; + readonly attribute bitmap32 featureMap = 65532; + readonly attribute int16u clusterRevision = 65533; + + request struct RequestCommissioningApprovalRequest { + int64u requestId = 0; + vendor_id vendorId = 1; + int16u productId = 2; + optional char_string<64> label = 3; + } + + request struct CommissionNodeRequest { + int64u requestId = 0; + int16u responseTimeoutSeconds = 1; + optional octet_string ipAddress = 2; + optional int16u port = 3; + } + + response struct ReverseOpenCommissioningWindow = 2 { + int16u commissioningTimeout = 0; + octet_string PAKEPasscodeVerifier = 1; + int16u discriminator = 2; + int32u iterations = 3; + octet_string<32> salt = 4; + } + + /** This command is sent by a client to request approval for a future CommissionNode call. */ + command access(invoke: manage) RequestCommissioningApproval(RequestCommissioningApprovalRequest): DefaultSuccess = 0; + /** This command is sent by a client to request that the server begins commissioning a previously approved request. */ + command access(invoke: manage) CommissionNode(CommissionNodeRequest): ReverseOpenCommissioningWindow = 1; +} + endpoint 0 { device type ma_rootdevice = 22, version 1; @@ -1647,6 +1698,21 @@ endpoint 0 { handle command KeySetReadAllIndices; handle command KeySetReadAllIndicesResponse; } + + server cluster CommissionerControl { + emits event CommissioningRequestResult; + ram attribute supportedDeviceCategories default = 0; + callback attribute generatedCommandList; + callback attribute acceptedCommandList; + callback attribute eventList; + callback attribute attributeList; + ram attribute featureMap default = 0; + ram attribute clusterRevision default = 1; + + handle command RequestCommissioningApproval; + handle command CommissionNode; + handle command ReverseOpenCommissioningWindow; + } } endpoint 1 { device type ma_aggregator = 14, version 1; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap index cbf31a039b85ee..6345745cde4fce 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap +++ b/examples/fabric-bridge-app/fabric-bridge-common/fabric-bridge-app.zap @@ -4003,6 +4003,164 @@ "reportableChange": 0 } ] + }, + { + "name": "Commissioner Control", + "code": 1873, + "mfgCode": null, + "define": "COMMISSIONER_CONTROL_CLUSTER", + "side": "server", + "enabled": 1, + "apiMaturity": "provisional", + "commands": [ + { + "name": "RequestCommissioningApproval", + "code": 0, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "CommissionNode", + "code": 1, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 + }, + { + "name": "ReverseOpenCommissioningWindow", + "code": 2, + "mfgCode": null, + "source": "server", + "isIncoming": 0, + "isEnabled": 1 + } + ], + "attributes": [ + { + "name": "SupportedDeviceCategories", + "code": 0, + "mfgCode": null, + "side": "server", + "type": "SupportedDeviceCategoryBitmap", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "GeneratedCommandList", + "code": 65528, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AcceptedCommandList", + "code": 65529, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "EventList", + "code": 65530, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "type": "array", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": null, + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "type": "bitmap32", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "ClusterRevision", + "code": 65533, + "mfgCode": null, + "side": "server", + "type": "int16u", + "included": 1, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "1", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + } + ], + "events": [ + { + "name": "CommissioningRequestResult", + "code": 0, + "mfgCode": null, + "side": "server", + "included": 1 + } + ] } ] }, diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp index 7a9521fa7820a7..ca3adddbc642ef 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp @@ -20,6 +20,7 @@ #include <app-common/zap-generated/cluster-objects.h> #include <app-common/zap-generated/ids/Attributes.h> #include <app-common/zap-generated/ids/Clusters.h> +#include <app/clusters/commissioner-control-server/commissioner-control-server.h> #include <lib/support/ZclString.h> using namespace ::chip; @@ -90,3 +91,13 @@ Protocols::InteractionModel::Status emberAfExternalAttributeWriteCallback(Endpoi return ret; } + +void MatterCommissionerControlPluginServerInitCallback() +{ + ChipLogProgress(Zcl, "MatterCommissionerControlPluginServerInitCallback"); + + BitMask<CommissionerControl::SupportedDeviceCategoryBitmap> supportedDeviceCategories; + supportedDeviceCategories.SetField(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization, 1); + CommissionerControl::CommissionerControlServer::Instance().SetSupportedDeviceCategoriesValue(kRootEndpointId, + supportedDeviceCategories); +} diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp new file mode 100644 index 00000000000000..d89c5729f9ada1 --- /dev/null +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -0,0 +1,251 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "commissioner-control-server.h" + +#include <protocols/interaction_model/StatusCode.h> + +#include <app-common/zap-generated/attributes/Accessors.h> +#include <app-common/zap-generated/cluster-enums.h> +#include <app-common/zap-generated/cluster-objects.h> +#include <app/AttributeAccessInterface.h> +#include <app/AttributeAccessInterfaceRegistry.h> +#include <app/CommandHandler.h> +#include <app/EventLogging.h> +#include <app/reporting/reporting.h> +#include <platform/PlatformManager.h> + +#include <memory> // For std::shared_ptr + +using namespace chip; +using namespace chip::app; + +using chip::Protocols::InteractionModel::Status; + +namespace { + +NodeId getNodeId(const app::CommandHandler * commandObj) +{ + if (nullptr == commandObj || nullptr == commandObj->GetExchangeContext()) + { + ChipLogError(Zcl, "Cannot access ExchangeContext of Command Object for Node ID"); + return kUndefinedNodeId; + } + + if (!commandObj->GetExchangeContext()->HasSessionHandle()) + { + ChipLogError(Zcl, "Cannot access session of Command Object for Node ID"); + return kUndefinedNodeId; + } + + auto descriptor = commandObj->GetExchangeContext()->GetSessionHandle()->GetSubjectDescriptor(); + if (descriptor.authMode != Access::AuthMode::kCase) + { + ChipLogError(Zcl, "Cannot get Node ID from non-CASE session of Command Object"); + return kUndefinedNodeId; + } + + return descriptor.subject; +} + +void AddReverseOpenCommissioningWindowResponse(CommandHandler * commandObj, const ConcreteCommandPath & path, + const Clusters::CommissionerControl::CommissioningWindowParams & params) +{ + Clusters::CommissionerControl::Commands::ReverseOpenCommissioningWindow::Type response; + response.commissioningTimeout = params.commissioningTimeout; + response.discriminator = params.discriminator; + response.iterations = params.iterations; + response.PAKEPasscodeVerifier = params.PAKEPasscodeVerifier; + response.salt = params.salt; + + commandObj->AddResponse(path, response); +} + +void RunDeferredCommissionNode(intptr_t commandArg) +{ + auto * info = reinterpret_cast<Clusters::CommissionerControl::CommissionNodeInfo *>(commandArg); + + Clusters::CommissionerControl::Delegate * delegate = + Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); + + if (delegate != nullptr) + { + CHIP_ERROR err = delegate->ReverseCommissionNode(info->params, info->ipAddress, info->port); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ReverseCommissionNode error: %s", err.AsString()); + } + } + else + { + ChipLogError(Zcl, "No delegate available for ReverseCommissionNode"); + } + + delete info; +} + +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +CommissionerControlServer CommissionerControlServer::instance; + +CommissionerControlServer & CommissionerControlServer::Instance() +{ + return instance; +} + +CHIP_ERROR CommissionerControlServer::Init(Delegate & delegate) +{ + mDelegate = &delegate; + return CHIP_NO_ERROR; +} + +Protocols::InteractionModel::Status CommissionerControlServer::GetSupportedDeviceCategoriesValue( + EndpointId endpoint, BitMask<SupportedDeviceCategoryBitmap> * supportedDeviceCategories) const +{ + Status status = Attributes::SupportedDeviceCategories::Get(endpoint, supportedDeviceCategories); + if (status != Status::Success) + { + ChipLogProgress(Zcl, "CommissionerControl: reading supportedDeviceCategories, err:0x%x", to_underlying(status)); + } + return status; +} + +Protocols::InteractionModel::Status +CommissionerControlServer::SetSupportedDeviceCategoriesValue(EndpointId endpoint, + const BitMask<SupportedDeviceCategoryBitmap> supportedDeviceCategories) +{ + Status status = Status::Success; + + if ((status = Attributes::SupportedDeviceCategories::Set(endpoint, supportedDeviceCategories)) != Status::Success) + { + ChipLogProgress(Zcl, "CommissionerControl: writing supportedDeviceCategories, err:0x%x", to_underlying(status)); + return status; + } + + return status; +} + +CHIP_ERROR CommissionerControlServer::EmitCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result) +{ + EventNumber eventNumber; + CHIP_ERROR error = LogEvent(result, kRootEndpointId, eventNumber); + if (CHIP_NO_ERROR != error) + { + ChipLogError(Zcl, "CommissionerControl: Unable to emit CommissioningRequestResult event: %s", error.AsString()); + } + + return error; +} + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip + +bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( + app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, + const Clusters::CommissionerControl::Commands::RequestCommissioningApproval::DecodableType & commandData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Status status = Status::Success; + + ChipLogProgress(Zcl, "Received command to request commissioning approval"); + + auto sourceNodeId = getNodeId(commandObj); + auto fabricIndex = commandObj->GetAccessingFabricIndex(); + auto requestId = commandData.requestId; + auto vendorId = commandData.vendorId; + auto productId = commandData.productId; + auto & label = commandData.label; + + // Create a CommissioningApprovalRequest struct and populate it with the command data + Clusters::CommissionerControl::CommissioningApprovalRequest request = { .requestId = requestId, + .vendorId = vendorId, + .productId = productId, + .clientNodeId = sourceNodeId, + .fabricIndex = fabricIndex, + .label = label }; + + Clusters::CommissionerControl::Delegate * delegate = + Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); + + VerifyOrExit(sourceNodeId != kUndefinedNodeId, err = CHIP_ERROR_WRONG_NODE_ID); + VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + // Handle commissioning approval request + err = delegate->HandleCommissioningApprovalRequest(request); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfCommissionerControlClusterRequestCommissioningApprovalCallback error: %s", err.AsString()); + status = Status::Failure; + } + + commandObj->AddStatus(commandPath, status); + return true; +} + +bool emberAfCommissionerControlClusterCommissionNodeCallback( + app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, + const Clusters::CommissionerControl::Commands::CommissionNode::DecodableType & commandData) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Status status = Status::Success; + + ChipLogProgress(Zcl, "Received command to commission node"); + + auto sourceNodeId = getNodeId(commandObj); + auto requestId = commandData.requestId; + + auto info = std::make_unique<Clusters::CommissionerControl::CommissionNodeInfo>(); + info->ipAddress = commandData.ipAddress; + info->port = commandData.port; + + Clusters::CommissionerControl::Delegate * delegate = + Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); + + VerifyOrExit(sourceNodeId != kUndefinedNodeId, err = CHIP_ERROR_WRONG_NODE_ID); + VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + + // Handle commissioning approval request + err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId, info->params); + VerifyOrExit(err == CHIP_NO_ERROR, err = CHIP_ERROR_INCORRECT_STATE); + + // Add the response for the commissioning window + AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, info->params); + + // Schedule the deferred reverse commission node task + DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, reinterpret_cast<intptr_t>(info.release())); + + return true; + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfCommissionerControlClusterCommissionNodeCallback error: %s", err.AsString()); + status = Status::Failure; + } + + commandObj->AddStatus(commandPath, status); + return true; +} diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h new file mode 100644 index 00000000000000..1e518b552e421f --- /dev/null +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -0,0 +1,99 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <app-common/zap-generated/cluster-objects.h> + +namespace chip { +namespace app { +namespace Clusters { +namespace CommissionerControl { + +struct CommissioningApprovalRequest +{ + uint64_t requestId; + VendorId vendorId; + uint16_t productId; + NodeId clientNodeId; + FabricIndex fabricIndex; + Optional<chip::CharSpan> label; +}; + +struct CommissioningWindowParams +{ + uint32_t iterations; + uint16_t commissioningTimeout; + uint16_t discriminator; + ByteSpan PAKEPasscodeVerifier; + ByteSpan salt; +}; + +struct CommissionNodeInfo +{ + CommissioningWindowParams params; + Optional<ByteSpan> ipAddress; + Optional<uint16_t> port; +}; + +class Delegate +{ +public: + // Command Delegates + virtual CHIP_ERROR HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) = 0; + virtual CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId, + CommissioningWindowParams & outParams) = 0; + virtual CHIP_ERROR ReverseCommissionNode(const CommissioningWindowParams & params, const Optional<ByteSpan> & ipAddress, + const Optional<uint16_t> & port) = 0; + virtual ~Delegate() = default; +}; + +class CommissionerControlServer +{ +public: + static CommissionerControlServer & Instance(); + + CHIP_ERROR Init(Delegate & delegate); + + Delegate * GetDelegate() { return mDelegate; } + + Protocols::InteractionModel::Status + GetSupportedDeviceCategoriesValue(EndpointId endpoint, + BitMask<SupportedDeviceCategoryBitmap> * supportedDeviceCategories) const; + + Protocols::InteractionModel::Status + SetSupportedDeviceCategoriesValue(EndpointId endpoint, const BitMask<SupportedDeviceCategoryBitmap> supportedDeviceCategories); + + /** + * @brief + * Called after the server return SUCCESS to a correctly formatted RequestCommissioningApproval command. + */ + CHIP_ERROR EmitCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result); + +private: + CommissionerControlServer() = default; + ~CommissionerControlServer() = default; + + static CommissionerControlServer instance; + + Delegate * mDelegate = nullptr; +}; + +} // namespace CommissionerControl +} // namespace Clusters +} // namespace app +} // namespace chip From 801ddc6981cc72ea6fd386a5dd1e3c78a1da1468 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Wed, 17 Jul 2024 18:23:22 -0700 Subject: [PATCH 02/14] Update src/app/clusters/commissioner-control-server/commissioner-control-server.cpp Co-authored-by: Terence Hampson <thampson@google.com> --- .../commissioner-control-server/commissioner-control-server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index d89c5729f9ada1..20f66f3cbc6b1d 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -229,7 +229,7 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( // Handle commissioning approval request err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId, info->params); - VerifyOrExit(err == CHIP_NO_ERROR, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(err == CHIP_NO_ERROR); // Add the response for the commissioning window AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, info->params); From c804c985ed72cd80f07dba7ea52b6943ca95516c Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Wed, 17 Jul 2024 20:33:24 -0700 Subject: [PATCH 03/14] Address review comments --- .../commissioner-control-server.cpp | 34 ++++--------------- .../commissioner-control-server.h | 2 +- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 20f66f3cbc6b1d..0b25c7db7fcdbf 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -29,7 +29,7 @@ #include <app/reporting/reporting.h> #include <platform/PlatformManager.h> -#include <memory> // For std::shared_ptr +#include <memory> using namespace chip; using namespace chip::app; @@ -40,24 +40,8 @@ namespace { NodeId getNodeId(const app::CommandHandler * commandObj) { - if (nullptr == commandObj || nullptr == commandObj->GetExchangeContext()) - { - ChipLogError(Zcl, "Cannot access ExchangeContext of Command Object for Node ID"); - return kUndefinedNodeId; - } - - if (!commandObj->GetExchangeContext()->HasSessionHandle()) - { - ChipLogError(Zcl, "Cannot access session of Command Object for Node ID"); - return kUndefinedNodeId; - } - - auto descriptor = commandObj->GetExchangeContext()->GetSessionHandle()->GetSubjectDescriptor(); - if (descriptor.authMode != Access::AuthMode::kCase) - { - ChipLogError(Zcl, "Cannot get Node ID from non-CASE session of Command Object"); - return kUndefinedNodeId; - } + VerifyOrDie(commandObj); + auto descriptor = commandObj->GetSubjectDescriptor(); return descriptor.subject; } @@ -105,11 +89,11 @@ namespace app { namespace Clusters { namespace CommissionerControl { -CommissionerControlServer CommissionerControlServer::instance; +CommissionerControlServer CommissionerControlServer::mInstance; CommissionerControlServer & CommissionerControlServer::Instance() { - return instance; + return mInstance; } CHIP_ERROR CommissionerControlServer::Init(Delegate & delegate) @@ -210,7 +194,6 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( const Clusters::CommissionerControl::Commands::CommissionNode::DecodableType & commandData) { CHIP_ERROR err = CHIP_NO_ERROR; - Status status = Status::Success; ChipLogProgress(Zcl, "Received command to commission node"); @@ -229,7 +212,7 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( // Handle commissioning approval request err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId, info->params); - VerifyOrExit(err == CHIP_NO_ERROR); + SuccessOrExit(err == CHIP_NO_ERROR); // Add the response for the commissioning window AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, info->params); @@ -237,15 +220,12 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( // Schedule the deferred reverse commission node task DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, reinterpret_cast<intptr_t>(info.release())); - return true; - exit: if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "emberAfCommissionerControlClusterCommissionNodeCallback error: %s", err.AsString()); - status = Status::Failure; + commandObj->AddStatus(commandPath, Status::Failure); } - commandObj->AddStatus(commandPath, status); return true; } diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index 1e518b552e421f..db9d596f5176a1 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -88,7 +88,7 @@ class CommissionerControlServer CommissionerControlServer() = default; ~CommissionerControlServer() = default; - static CommissionerControlServer instance; + static CommissionerControlServer mInstance; Delegate * mDelegate = nullptr; }; From 6f17e1deaf38bf4c8d10e5dc9d6cab8283eddb07 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Thu, 18 Jul 2024 10:08:11 -0700 Subject: [PATCH 04/14] Update src/app/clusters/commissioner-control-server/commissioner-control-server.cpp Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> --- .../commissioner-control-server/commissioner-control-server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 0b25c7db7fcdbf..767ff1be2fc38a 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -128,7 +128,7 @@ CommissionerControlServer::SetSupportedDeviceCategoriesValue(EndpointId endpoint return status; } -CHIP_ERROR CommissionerControlServer::EmitCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result) +CHIP_ERROR CommissionerControlServer::GenerateCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result) { EventNumber eventNumber; CHIP_ERROR error = LogEvent(result, kRootEndpointId, eventNumber); From 4f6c80dc9d8ac6243b02b6c0df133f8b980b35e0 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Thu, 18 Jul 2024 10:08:47 -0700 Subject: [PATCH 05/14] Update src/app/clusters/commissioner-control-server/commissioner-control-server.h Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> --- .../commissioner-control-server/commissioner-control-server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index db9d596f5176a1..b3f568ba5ce9ff 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -31,7 +31,7 @@ struct CommissioningApprovalRequest uint16_t productId; NodeId clientNodeId; FabricIndex fabricIndex; - Optional<chip::CharSpan> label; + Optional<CharSpan> label; }; struct CommissioningWindowParams From b4513058bbc429f858a1a6f574e03608c8f62889 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Thu, 18 Jul 2024 12:07:34 -0700 Subject: [PATCH 06/14] Move MatterCommissionerControlPluginServerInitCallback from application to SDK --- .../fabric-bridge-common/src/ZCLCallbacks.cpp | 11 ------- examples/fabric-bridge-app/linux/main.cpp | 13 ++++++++ .../commissioner-control-server.cpp | 31 +++++++++++-------- .../commissioner-control-server.h | 2 +- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp index ca3adddbc642ef..7a9521fa7820a7 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/ZCLCallbacks.cpp @@ -20,7 +20,6 @@ #include <app-common/zap-generated/cluster-objects.h> #include <app-common/zap-generated/ids/Attributes.h> #include <app-common/zap-generated/ids/Clusters.h> -#include <app/clusters/commissioner-control-server/commissioner-control-server.h> #include <lib/support/ZclString.h> using namespace ::chip; @@ -91,13 +90,3 @@ Protocols::InteractionModel::Status emberAfExternalAttributeWriteCallback(Endpoi return ret; } - -void MatterCommissionerControlPluginServerInitCallback() -{ - ChipLogProgress(Zcl, "MatterCommissionerControlPluginServerInitCallback"); - - BitMask<CommissionerControl::SupportedDeviceCategoryBitmap> supportedDeviceCategories; - supportedDeviceCategories.SetField(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization, 1); - CommissionerControl::CommissionerControlServer::Instance().SetSupportedDeviceCategoriesValue(kRootEndpointId, - supportedDeviceCategories); -} diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 0aa22b8445ee56..6312b3367aad38 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -23,6 +23,7 @@ #include "DeviceManager.h" #include <app/AttributeAccessInterfaceRegistry.h> +#include <app/clusters/commissioner-control-server/commissioner-control-server.h> #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE #include "RpcClient.h" @@ -167,6 +168,18 @@ void ApplicationInit() pollingThread.detach(); DeviceMgr().Init(); + + ChipLogProgress(Zcl, "Initialize SupportedDeviceCategories of Commissioner Control Cluster for this device."); + + BitMask<CommissionerControl::SupportedDeviceCategoryBitmap> supportedDeviceCategories; + supportedDeviceCategories.SetField(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization, 1); + Protocols::InteractionModel::Status status = + CommissionerControl::CommissionerControlServer::Instance().SetSupportedDeviceCategoriesValue(kRootEndpointId, + supportedDeviceCategories); + if (status != Protocols::InteractionModel::Status::Success) + { + ChipLogError(NotSpecified, "Failed to set SupportedDeviceCategories: %d", static_cast<int>(status)); + } } void ApplicationShutdown() diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 767ff1be2fc38a..1f562d2fbcc5a2 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -38,14 +38,6 @@ using chip::Protocols::InteractionModel::Status; namespace { -NodeId getNodeId(const app::CommandHandler * commandObj) -{ - VerifyOrDie(commandObj); - auto descriptor = commandObj->GetSubjectDescriptor(); - - return descriptor.subject; -} - void AddReverseOpenCommissioningWindowResponse(CommandHandler * commandObj, const ConcreteCommandPath & path, const Clusters::CommissionerControl::CommissioningWindowParams & params) { @@ -128,7 +120,8 @@ CommissionerControlServer::SetSupportedDeviceCategoriesValue(EndpointId endpoint return status; } -CHIP_ERROR CommissionerControlServer::GenerateCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result) +CHIP_ERROR +CommissionerControlServer::GenerateCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result) { EventNumber eventNumber; CHIP_ERROR error = LogEvent(result, kRootEndpointId, eventNumber); @@ -154,12 +147,16 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( ChipLogProgress(Zcl, "Received command to request commissioning approval"); - auto sourceNodeId = getNodeId(commandObj); + auto descriptor = commandObj->GetSubjectDescriptor(); + auto sourceNodeId = descriptor.subject; auto fabricIndex = commandObj->GetAccessingFabricIndex(); auto requestId = commandData.requestId; auto vendorId = commandData.vendorId; auto productId = commandData.productId; - auto & label = commandData.label; + + // The label assigned from commandData need to be stored in CommissionerControl::Delegate which ensure that the backing buffer + // of it has a valid lifespan during fabric sync setup process. + auto & label = commandData.label; // Create a CommissioningApprovalRequest struct and populate it with the command data Clusters::CommissionerControl::CommissioningApprovalRequest request = { .requestId = requestId, @@ -197,7 +194,8 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( ChipLogProgress(Zcl, "Received command to commission node"); - auto sourceNodeId = getNodeId(commandObj); + auto descriptor = commandObj->GetSubjectDescriptor(); + auto sourceNodeId = descriptor.subject; auto requestId = commandData.requestId; auto info = std::make_unique<Clusters::CommissionerControl::CommissionNodeInfo>(); @@ -210,7 +208,9 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( VerifyOrExit(sourceNodeId != kUndefinedNodeId, err = CHIP_ERROR_WRONG_NODE_ID); VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - // Handle commissioning approval request + // Handle commissioning approval request, the ipAddress assigned from commandData need to be stored in + // CommissionerControl::Delegate which ensure that the backing buffer of ipAddress has a valid lifespan until the deferred task + // is executed. err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId, info->params); SuccessOrExit(err == CHIP_NO_ERROR); @@ -229,3 +229,8 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( return true; } + +void MatterCommissionerControlPluginServerInitCallback() +{ + ChipLogProgress(Zcl, "Initiating Commissioner Control cluster."); +} diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index b3f568ba5ce9ff..9ded85c533831e 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -82,7 +82,7 @@ class CommissionerControlServer * @brief * Called after the server return SUCCESS to a correctly formatted RequestCommissioningApproval command. */ - CHIP_ERROR EmitCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result); + CHIP_ERROR GenerateCommissioningRequestResultEvent(const Events::CommissioningRequestResult::Type & result); private: CommissionerControlServer() = default; From d96cfbcd66e368f7ae18b6fb311658d043df3841 Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Thu, 18 Jul 2024 14:27:22 -0700 Subject: [PATCH 07/14] Return kUndefinedNodeId if get the NodeID from a group or PASE session. --- examples/fabric-bridge-app/linux/main.cpp | 13 --- .../commissioner-control-server.cpp | 53 ++++++++--- .../commissioner-control-server.h | 91 ++++++++++++++++++- 3 files changed, 125 insertions(+), 32 deletions(-) diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 6312b3367aad38..0aa22b8445ee56 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -23,7 +23,6 @@ #include "DeviceManager.h" #include <app/AttributeAccessInterfaceRegistry.h> -#include <app/clusters/commissioner-control-server/commissioner-control-server.h> #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE #include "RpcClient.h" @@ -168,18 +167,6 @@ void ApplicationInit() pollingThread.detach(); DeviceMgr().Init(); - - ChipLogProgress(Zcl, "Initialize SupportedDeviceCategories of Commissioner Control Cluster for this device."); - - BitMask<CommissionerControl::SupportedDeviceCategoryBitmap> supportedDeviceCategories; - supportedDeviceCategories.SetField(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization, 1); - Protocols::InteractionModel::Status status = - CommissionerControl::CommissionerControlServer::Instance().SetSupportedDeviceCategoriesValue(kRootEndpointId, - supportedDeviceCategories); - if (status != Protocols::InteractionModel::Status::Success) - { - ChipLogError(NotSpecified, "Failed to set SupportedDeviceCategories: %d", static_cast<int>(status)); - } } void ApplicationShutdown() diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 1f562d2fbcc5a2..d9be46efcc9aa5 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -38,6 +38,32 @@ using chip::Protocols::InteractionModel::Status; namespace { +NodeId getNodeId(const app::CommandHandler * commandObj) +{ + if (nullptr == commandObj || nullptr == commandObj->GetExchangeContext()) + { + ChipLogError(Zcl, "Cannot access ExchangeContext of Command Object for Node ID"); + return kUndefinedNodeId; + } + + if (!commandObj->GetExchangeContext()->HasSessionHandle()) + { + ChipLogError(Zcl, "Cannot access session of Command Object for Node ID"); + return kUndefinedNodeId; + } + + auto descriptor = commandObj->GetExchangeContext()->GetSessionHandle()->GetSubjectDescriptor(); + + // Return kUndefinedNodeId if get the NodeID from a group or PASE session. + if (descriptor.authMode != Access::AuthMode::kCase) + { + ChipLogError(Zcl, "Cannot get Node ID from non-CASE session of Command Object"); + return kUndefinedNodeId; + } + + return descriptor.subject; +} + void AddReverseOpenCommissioningWindowResponse(CommandHandler * commandObj, const ConcreteCommandPath & path, const Clusters::CommissionerControl::CommissioningWindowParams & params) { @@ -147,8 +173,7 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( ChipLogProgress(Zcl, "Received command to request commissioning approval"); - auto descriptor = commandObj->GetSubjectDescriptor(); - auto sourceNodeId = descriptor.subject; + auto sourceNodeId = getNodeId(commandObj); auto fabricIndex = commandObj->GetAccessingFabricIndex(); auto requestId = commandData.requestId; auto vendorId = commandData.vendorId; @@ -194,13 +219,10 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( ChipLogProgress(Zcl, "Received command to commission node"); - auto descriptor = commandObj->GetSubjectDescriptor(); - auto sourceNodeId = descriptor.subject; + auto sourceNodeId = getNodeId(commandObj); auto requestId = commandData.requestId; - auto info = std::make_unique<Clusters::CommissionerControl::CommissionNodeInfo>(); - info->ipAddress = commandData.ipAddress; - info->port = commandData.port; + auto commissionNodeInfo = std::make_unique<Clusters::CommissionerControl::CommissionNodeInfo>(); Clusters::CommissionerControl::Delegate * delegate = Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); @@ -208,17 +230,20 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( VerifyOrExit(sourceNodeId != kUndefinedNodeId, err = CHIP_ERROR_WRONG_NODE_ID); VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - // Handle commissioning approval request, the ipAddress assigned from commandData need to be stored in - // CommissionerControl::Delegate which ensure that the backing buffer of ipAddress has a valid lifespan until the deferred task - // is executed. - err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId, info->params); + // Set IP address and port in the CommissionNodeInfo struct + commissionNodeInfo->port = commandData.port; + err = commissionNodeInfo->SetIPAddress(commandData.ipAddress); + SuccessOrExit(err == CHIP_NO_ERROR); + + // Validate the commission node command. + err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId); SuccessOrExit(err == CHIP_NO_ERROR); - // Add the response for the commissioning window - AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, info->params); + // Add the response for the commissioning window. + AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, commissionNodeInfo->params); // Schedule the deferred reverse commission node task - DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, reinterpret_cast<intptr_t>(info.release())); + DeviceLayer::PlatformMgr().ScheduleWork(RunDeferredCommissionNode, reinterpret_cast<intptr_t>(commissionNodeInfo.release())); exit: if (err != CHIP_NO_ERROR) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index 9ded85c533831e..35b413d5810f91 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -24,6 +24,9 @@ namespace app { namespace Clusters { namespace CommissionerControl { +// Set to 64 bytes, which is more than enough for storing an IPv6 address in either binary or text form. +static constexpr size_t kIpAddressBufferSize = 64; + struct CommissioningApprovalRequest { uint64_t requestId; @@ -48,18 +51,96 @@ struct CommissionNodeInfo CommissioningWindowParams params; Optional<ByteSpan> ipAddress; Optional<uint16_t> port; + uint8_t ipAddressBuffer[kIpAddressBufferSize]; + + CHIP_ERROR SetIPAddress(const Optional<ByteSpan> & address) + { + if (!address.HasValue()) + { + ipAddress.ClearValue(); + return CHIP_NO_ERROR; + } + + const ByteSpan & addressSpan = address.Value(); + size_t addressLength = addressSpan.size(); + + if (addressLength > kIpAddressBufferSize) + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + if (addressLength == 0) + { + ipAddress.ClearValue(); + return CHIP_NO_ERROR; + } + + memcpy(ipAddressBuffer, addressSpan.data(), addressLength); + ipAddress.SetValue(ByteSpan(ipAddressBuffer, addressLength)); + return CHIP_NO_ERROR; + } }; class Delegate { public: - // Command Delegates + /** + * @brief Handle a commissioning approval request. + * + * This command is sent by a client to request approval for a future CommissionNode call. + * The server SHALL always return SUCCESS to a correctly formatted RequestCommissioningApproval + * command, and then send a CommissioningRequestResult event once the result is ready. + * + * @param request The commissioning approval request to handle. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ virtual CHIP_ERROR HandleCommissioningApprovalRequest(const CommissioningApprovalRequest & request) = 0; - virtual CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId, - CommissioningWindowParams & outParams) = 0; + + /** + * @brief Validate a commission node command. + * + * This command is sent by a client to request that the server begins commissioning a previously + * approved request. + * + * The server SHALL return FAILURE if the CommissionNode command is not sent from the same + * NodeId as the RequestCommissioningApproval or if the provided RequestId to CommissionNode + * does not match the value provided to RequestCommissioningApproval. + * + * The validation SHALL fail if the client Node ID is kUndefinedNodeId, such as getting the NodeID from + * a group or PASE session. + * + * @param clientNodeId The NodeId of the client. + * @param requestId The request ID to validate. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) = 0; + + /** + * @brief Get the parameters for the commissioning window. + * + * This method is called to retrieve the parameters needed for the commissioning window. + * + * @param[out] outParams The parameters for the commissioning window. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) = 0; + + /** + * @brief Reverse the commission node process. + * + * When received within the timeout specified by CommissionNode, the client SHALL open a + * commissioning window on the node which the client called RequestCommissioningApproval to + * have commissioned. + * + * @param params The parameters for the commissioning window. + * @param ipAddress Optional IP address for the commissioning window. + * @param port Optional port for the commissioning window. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ virtual CHIP_ERROR ReverseCommissionNode(const CommissioningWindowParams & params, const Optional<ByteSpan> & ipAddress, - const Optional<uint16_t> & port) = 0; - virtual ~Delegate() = default; + const Optional<uint16_t> & port) = 0; + + virtual ~Delegate() = default; }; class CommissionerControlServer From bff49d6746a21f85be925a523661d629ff75daed Mon Sep 17 00:00:00 2001 From: Yufeng Wang <yufengwang@google.com> Date: Thu, 25 Jul 2024 02:44:46 +0200 Subject: [PATCH 08/14] Add case requirement to CommissionerControlCluster --- .../commissioner-control-server.cpp | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index d9be46efcc9aa5..e56a98a05aebdb 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -174,6 +174,15 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( ChipLogProgress(Zcl, "Received command to request commissioning approval"); auto sourceNodeId = getNodeId(commandObj); + + // Check if the command is executed via a CASE session + if (sourceNodeId == kUndefinedNodeId) + { + ChipLogError(Zcl, "Command not executed via CASE session, failing with UNSUPPORTED_ACCESS"); + commandObj->AddStatus(commandPath, Status::UnsupportedAccess); + return true; + } + auto fabricIndex = commandObj->GetAccessingFabricIndex(); auto requestId = commandData.requestId; auto vendorId = commandData.vendorId; @@ -194,7 +203,6 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( Clusters::CommissionerControl::Delegate * delegate = Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); - VerifyOrExit(sourceNodeId != kUndefinedNodeId, err = CHIP_ERROR_WRONG_NODE_ID); VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); // Handle commissioning approval request @@ -220,6 +228,15 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( ChipLogProgress(Zcl, "Received command to commission node"); auto sourceNodeId = getNodeId(commandObj); + + // Check if the command is executed via a CASE session + if (sourceNodeId == kUndefinedNodeId) + { + ChipLogError(Zcl, "Command not executed via CASE session, failing with UNSUPPORTED_ACCESS"); + commandObj->AddStatus(commandPath, Status::UnsupportedAccess); + return true; + } + auto requestId = commandData.requestId; auto commissionNodeInfo = std::make_unique<Clusters::CommissionerControl::CommissionNodeInfo>(); @@ -227,7 +244,6 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( Clusters::CommissionerControl::Delegate * delegate = Clusters::CommissionerControl::CommissionerControlServer::Instance().GetDelegate(); - VerifyOrExit(sourceNodeId != kUndefinedNodeId, err = CHIP_ERROR_WRONG_NODE_ID); VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INCORRECT_STATE); // Set IP address and port in the CommissionNodeInfo struct From b0cbeb12099c30c082b35f3671607aa23b9f620f Mon Sep 17 00:00:00 2001 From: "Restyled.io" <commits@restyled.io> Date: Thu, 25 Jul 2024 00:45:37 +0000 Subject: [PATCH 09/14] Restyled by clang-format --- .../commissioner-control-server.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index e56a98a05aebdb..ba40913d166a64 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -183,10 +183,10 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( return true; } - auto fabricIndex = commandObj->GetAccessingFabricIndex(); - auto requestId = commandData.requestId; - auto vendorId = commandData.vendorId; - auto productId = commandData.productId; + auto fabricIndex = commandObj->GetAccessingFabricIndex(); + auto requestId = commandData.requestId; + auto vendorId = commandData.vendorId; + auto productId = commandData.productId; // The label assigned from commandData need to be stored in CommissionerControl::Delegate which ensure that the backing buffer // of it has a valid lifespan during fabric sync setup process. @@ -237,7 +237,7 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( return true; } - auto requestId = commandData.requestId; + auto requestId = commandData.requestId; auto commissionNodeInfo = std::make_unique<Clusters::CommissionerControl::CommissionNodeInfo>(); From e06ce4a19e8dcf62382d5a4b1512a76bef57fde1 Mon Sep 17 00:00:00 2001 From: Terence Hampson <thampson@google.com> Date: Thu, 25 Jul 2024 22:19:27 +0000 Subject: [PATCH 10/14] Address PR comments --- .../commissioner-control-server.cpp | 49 +++++++------------ .../commissioner-control-server.h | 42 ++++++---------- 2 files changed, 33 insertions(+), 58 deletions(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index ba40913d166a64..99a98ff4e718fb 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -38,29 +38,14 @@ using chip::Protocols::InteractionModel::Status; namespace { -NodeId getNodeId(const app::CommandHandler * commandObj) +NodeId GetNodeId(const CommandHandler * commandObj) { - if (nullptr == commandObj || nullptr == commandObj->GetExchangeContext()) - { - ChipLogError(Zcl, "Cannot access ExchangeContext of Command Object for Node ID"); - return kUndefinedNodeId; - } + auto descriptor = commandObj->GetSubjectDescriptor(); - if (!commandObj->GetExchangeContext()->HasSessionHandle()) - { - ChipLogError(Zcl, "Cannot access session of Command Object for Node ID"); - return kUndefinedNodeId; - } - - auto descriptor = commandObj->GetExchangeContext()->GetSessionHandle()->GetSubjectDescriptor(); - - // Return kUndefinedNodeId if get the NodeID from a group or PASE session. if (descriptor.authMode != Access::AuthMode::kCase) { - ChipLogError(Zcl, "Cannot get Node ID from non-CASE session of Command Object"); return kUndefinedNodeId; } - return descriptor.subject; } @@ -86,10 +71,10 @@ void RunDeferredCommissionNode(intptr_t commandArg) if (delegate != nullptr) { - CHIP_ERROR err = delegate->ReverseCommissionNode(info->params, info->ipAddress, info->port); + CHIP_ERROR err = delegate->ReverseCommissionNode(info->params, info->GetIPAddress(), info->port); if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "ReverseCommissionNode error: %s", err.AsString()); + ChipLogError(Zcl, "ReverseCommissionNode error: %" CHIP_ERROR_FORMAT, err.Format()); } } else @@ -107,11 +92,11 @@ namespace app { namespace Clusters { namespace CommissionerControl { -CommissionerControlServer CommissionerControlServer::mInstance; +CommissionerControlServer CommissionerControlServer::sInstance; CommissionerControlServer & CommissionerControlServer::Instance() { - return mInstance; + return sInstance; } CHIP_ERROR CommissionerControlServer::Init(Delegate & delegate) @@ -120,7 +105,7 @@ CHIP_ERROR CommissionerControlServer::Init(Delegate & delegate) return CHIP_NO_ERROR; } -Protocols::InteractionModel::Status CommissionerControlServer::GetSupportedDeviceCategoriesValue( +Status CommissionerControlServer::GetSupportedDeviceCategoriesValue( EndpointId endpoint, BitMask<SupportedDeviceCategoryBitmap> * supportedDeviceCategories) const { Status status = Attributes::SupportedDeviceCategories::Get(endpoint, supportedDeviceCategories); @@ -131,7 +116,7 @@ Protocols::InteractionModel::Status CommissionerControlServer::GetSupportedDevic return status; } -Protocols::InteractionModel::Status +Status CommissionerControlServer::SetSupportedDeviceCategoriesValue(EndpointId endpoint, const BitMask<SupportedDeviceCategoryBitmap> supportedDeviceCategories) { @@ -173,12 +158,12 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( ChipLogProgress(Zcl, "Received command to request commissioning approval"); - auto sourceNodeId = getNodeId(commandObj); + auto sourceNodeId = GetNodeId(commandObj); // Check if the command is executed via a CASE session if (sourceNodeId == kUndefinedNodeId) { - ChipLogError(Zcl, "Command not executed via CASE session, failing with UNSUPPORTED_ACCESS"); + ChipLogError(Zcl, "Commissioning approval request not executed via CASE session, failing with UNSUPPORTED_ACCESS"); commandObj->AddStatus(commandPath, Status::UnsupportedAccess); return true; } @@ -211,8 +196,8 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( exit: if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "emberAfCommissionerControlClusterRequestCommissioningApprovalCallback error: %s", err.AsString()); - status = Status::Failure; + ChipLogError(Zcl, "emberAfCommissionerControlClusterRequestCommissioningApprovalCallback error: %" CHIP_ERROR_FORMAT, err.Format()); + status = StatusIB(err).mStatus; } commandObj->AddStatus(commandPath, status); @@ -227,12 +212,12 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( ChipLogProgress(Zcl, "Received command to commission node"); - auto sourceNodeId = getNodeId(commandObj); + auto sourceNodeId = GetNodeId(commandObj); // Check if the command is executed via a CASE session if (sourceNodeId == kUndefinedNodeId) { - ChipLogError(Zcl, "Command not executed via CASE session, failing with UNSUPPORTED_ACCESS"); + ChipLogError(Zcl, "Commission node request not executed via CASE session, failing with UNSUPPORTED_ACCESS"); commandObj->AddStatus(commandPath, Status::UnsupportedAccess); return true; } @@ -264,8 +249,8 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( exit: if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "emberAfCommissionerControlClusterCommissionNodeCallback error: %s", err.AsString()); - commandObj->AddStatus(commandPath, Status::Failure); + ChipLogError(Zcl, "emberAfCommissionerControlClusterCommissionNodeCallback error: %" CHIP_ERROR_FORMAT, err.Format()); + commandObj->AddStatus(commandPath, StatusIB(err).mStatus); } return true; @@ -273,5 +258,5 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( void MatterCommissionerControlPluginServerInitCallback() { - ChipLogProgress(Zcl, "Initiating Commissioner Control cluster."); + ChipLogProgress(Zcl, "Initializing Commissioner Control cluster."); } diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index 35b413d5810f91..1edc55d90110ca 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -24,8 +24,8 @@ namespace app { namespace Clusters { namespace CommissionerControl { -// Set to 64 bytes, which is more than enough for storing an IPv6 address in either binary or text form. -static constexpr size_t kIpAddressBufferSize = 64; +// Spec indicates that IP Address is either 4 or 16 bytes. +static constexpr size_t kIpAddressBufferSize = 16; struct CommissioningApprovalRequest { @@ -48,10 +48,10 @@ struct CommissioningWindowParams struct CommissionNodeInfo { - CommissioningWindowParams params; - Optional<ByteSpan> ipAddress; - Optional<uint16_t> port; - uint8_t ipAddressBuffer[kIpAddressBufferSize]; + const Optional<ByteSpan> GetIPAddress() + { + return ipAddress; + } CHIP_ERROR SetIPAddress(const Optional<ByteSpan> & address) { @@ -63,22 +63,22 @@ struct CommissionNodeInfo const ByteSpan & addressSpan = address.Value(); size_t addressLength = addressSpan.size(); - - if (addressLength > kIpAddressBufferSize) - { - return CHIP_ERROR_BUFFER_TOO_SMALL; - } - - if (addressLength == 0) + if (addressLength != 4 && addressLength != 16) { - ipAddress.ClearValue(); - return CHIP_NO_ERROR; + return CHIP_ERROR_INVALID_ARGUMENT; } memcpy(ipAddressBuffer, addressSpan.data(), addressLength); ipAddress.SetValue(ByteSpan(ipAddressBuffer, addressLength)); return CHIP_NO_ERROR; } + + CommissioningWindowParams params; + Optional<uint16_t> port; + +protected: + Optional<ByteSpan> ipAddress; + uint8_t ipAddressBuffer[kIpAddressBufferSize]; }; class Delegate @@ -115,16 +115,6 @@ class Delegate */ virtual CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) = 0; - /** - * @brief Get the parameters for the commissioning window. - * - * This method is called to retrieve the parameters needed for the commissioning window. - * - * @param[out] outParams The parameters for the commissioning window. - * @return CHIP_ERROR indicating the success or failure of the operation. - */ - virtual CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) = 0; - /** * @brief Reverse the commission node process. * @@ -169,7 +159,7 @@ class CommissionerControlServer CommissionerControlServer() = default; ~CommissionerControlServer() = default; - static CommissionerControlServer mInstance; + static CommissionerControlServer sInstance; Delegate * mDelegate = nullptr; }; From d56a7f756c643c420e8e91e9a4e0bc20e5d535ab Mon Sep 17 00:00:00 2001 From: Terence Hampson <thampson@google.com> Date: Thu, 25 Jul 2024 22:30:01 +0000 Subject: [PATCH 11/14] Few more fixes --- .../commissioner-control-server.cpp | 6 +++--- .../commissioner-control-server.h | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 99a98ff4e718fb..1fba69e8962432 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -71,7 +71,7 @@ void RunDeferredCommissionNode(intptr_t commandArg) if (delegate != nullptr) { - CHIP_ERROR err = delegate->ReverseCommissionNode(info->params, info->GetIPAddress(), info->port); + CHIP_ERROR err = delegate->ReverseCommissionNode(info->params, info->ipAddress.GetIPAddress(), info->port); if (err != CHIP_NO_ERROR) { ChipLogError(Zcl, "ReverseCommissionNode error: %" CHIP_ERROR_FORMAT, err.Format()); @@ -138,7 +138,7 @@ CommissionerControlServer::GenerateCommissioningRequestResultEvent(const Events: CHIP_ERROR error = LogEvent(result, kRootEndpointId, eventNumber); if (CHIP_NO_ERROR != error) { - ChipLogError(Zcl, "CommissionerControl: Unable to emit CommissioningRequestResult event: %s", error.AsString()); + ChipLogError(Zcl, "CommissionerControl: Unable to emit CommissioningRequestResult event: %" CHIP_ERROR_FORMAT, error.Format()); } return error; @@ -233,7 +233,7 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( // Set IP address and port in the CommissionNodeInfo struct commissionNodeInfo->port = commandData.port; - err = commissionNodeInfo->SetIPAddress(commandData.ipAddress); + err = commissionNodeInfo->ipAddress.SetIPAddress(commandData.ipAddress); SuccessOrExit(err == CHIP_NO_ERROR); // Validate the commission node command. diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index 1edc55d90110ca..c5ae15b0b64cc3 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -46,8 +46,8 @@ struct CommissioningWindowParams ByteSpan salt; }; -struct CommissionNodeInfo -{ +class ProtectedIPAddress { +public: const Optional<ByteSpan> GetIPAddress() { return ipAddress; @@ -73,14 +73,18 @@ struct CommissionNodeInfo return CHIP_NO_ERROR; } - CommissioningWindowParams params; - Optional<uint16_t> port; - -protected: +private: Optional<ByteSpan> ipAddress; uint8_t ipAddressBuffer[kIpAddressBufferSize]; }; +struct CommissionNodeInfo +{ + CommissioningWindowParams params; + ProtectedIPAddress ipAddress; + Optional<uint16_t> port; +}; + class Delegate { public: From 53b1e8a47feabf9976a5b82bf9bc35e57fa5840a Mon Sep 17 00:00:00 2001 From: "Restyled.io" <commits@restyled.io> Date: Thu, 25 Jul 2024 22:30:23 +0000 Subject: [PATCH 12/14] Restyled by clang-format --- .../commissioner-control-server.cpp | 6 ++++-- .../commissioner-control-server.h | 8 +++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 1fba69e8962432..937996187ce3ec 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -138,7 +138,8 @@ CommissionerControlServer::GenerateCommissioningRequestResultEvent(const Events: CHIP_ERROR error = LogEvent(result, kRootEndpointId, eventNumber); if (CHIP_NO_ERROR != error) { - ChipLogError(Zcl, "CommissionerControl: Unable to emit CommissioningRequestResult event: %" CHIP_ERROR_FORMAT, error.Format()); + ChipLogError(Zcl, "CommissionerControl: Unable to emit CommissioningRequestResult event: %" CHIP_ERROR_FORMAT, + error.Format()); } return error; @@ -196,7 +197,8 @@ bool emberAfCommissionerControlClusterRequestCommissioningApprovalCallback( exit: if (err != CHIP_NO_ERROR) { - ChipLogError(Zcl, "emberAfCommissionerControlClusterRequestCommissioningApprovalCallback error: %" CHIP_ERROR_FORMAT, err.Format()); + ChipLogError(Zcl, "emberAfCommissionerControlClusterRequestCommissioningApprovalCallback error: %" CHIP_ERROR_FORMAT, + err.Format()); status = StatusIB(err).mStatus; } diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index c5ae15b0b64cc3..3a8d5e051ea9af 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -46,12 +46,10 @@ struct CommissioningWindowParams ByteSpan salt; }; -class ProtectedIPAddress { +class ProtectedIPAddress +{ public: - const Optional<ByteSpan> GetIPAddress() - { - return ipAddress; - } + const Optional<ByteSpan> GetIPAddress() { return ipAddress; } CHIP_ERROR SetIPAddress(const Optional<ByteSpan> & address) { From 165f4dc524e494ce96184375dc02c679dc427f39 Mon Sep 17 00:00:00 2001 From: Terence Hampson <thampson@google.com> Date: Fri, 26 Jul 2024 01:26:53 +0000 Subject: [PATCH 13/14] Add GetCommissioningWindowParams back in --- .../commissioner-control-server.cpp | 4 ++++ .../commissioner-control-server.h | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 937996187ce3ec..69b9384ff76718 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -242,6 +242,10 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( err = delegate->ValidateCommissionNodeCommand(sourceNodeId, requestId); SuccessOrExit(err == CHIP_NO_ERROR); + // Populate the parameters for the commissioning window + err = delegate->GetCommissioningWindowParams(commissionNodeInfo->params); + SuccessOrExit(err == CHIP_NO_ERROR); + // Add the response for the commissioning window. AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, commissionNodeInfo->params); diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.h b/src/app/clusters/commissioner-control-server/commissioner-control-server.h index 3a8d5e051ea9af..5e2422f5ebe018 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.h +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.h @@ -117,6 +117,16 @@ class Delegate */ virtual CHIP_ERROR ValidateCommissionNodeCommand(NodeId clientNodeId, uint64_t requestId) = 0; + /** + * @brief Get the parameters for the commissioning window. + * + * This method is called to retrieve the parameters needed for the commissioning window. + * + * @param[out] outParams The parameters for the commissioning window. + * @return CHIP_ERROR indicating the success or failure of the operation. + */ + virtual CHIP_ERROR GetCommissioningWindowParams(CommissioningWindowParams & outParams) = 0; + /** * @brief Reverse the commission node process. * From 73c331edbdc84a1eade0d26151da7dbad855a6a4 Mon Sep 17 00:00:00 2001 From: "Restyled.io" <commits@restyled.io> Date: Fri, 26 Jul 2024 01:27:22 +0000 Subject: [PATCH 14/14] Restyled by whitespace --- .../commissioner-control-server/commissioner-control-server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp index 69b9384ff76718..98616f9e0e281b 100644 --- a/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp +++ b/src/app/clusters/commissioner-control-server/commissioner-control-server.cpp @@ -244,7 +244,7 @@ bool emberAfCommissionerControlClusterCommissionNodeCallback( // Populate the parameters for the commissioning window err = delegate->GetCommissioningWindowParams(commissionNodeInfo->params); - SuccessOrExit(err == CHIP_NO_ERROR); + SuccessOrExit(err == CHIP_NO_ERROR); // Add the response for the commissioning window. AddReverseOpenCommissioningWindowResponse(commandObj, commandPath, commissionNodeInfo->params);