Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[icd] Add steps for checking ICD during commissioning #29427

Merged
42 changes: 32 additions & 10 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,8 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
// Per the spec, we restart from after adding the NOC.
return GetNextCommissioningStage(CommissioningStage::kSendNOC, lastErr);
}
if (mParams.GetCheckForMatchingFabric())
{
return CommissioningStage::kCheckForMatchingFabric;
}
return CommissioningStage::kArmFailsafe;
case CommissioningStage::kCheckForMatchingFabric:
return CommissioningStage::kReadCommissioningInfo2;
case CommissioningStage::kReadCommissioningInfo2:
return CommissioningStage::kArmFailsafe;
case CommissioningStage::kArmFailsafe:
return CommissioningStage::kConfigRegulatory;
Expand Down Expand Up @@ -635,11 +631,37 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
// Don't send DST unless the device says it needs it
mNeedsDST = false;
break;
case CommissioningStage::kCheckForMatchingFabric: {
chip::NodeId nodeId = report.Get<MatchingFabricInfo>().nodeId;
if (nodeId != kUndefinedNodeId)
case CommissioningStage::kReadCommissioningInfo2: {
bool shouldReadCommissioningInfo2 =
mParams.GetCheckForMatchingFabric() || (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore);
if (shouldReadCommissioningInfo2)
yunhanw-google marked this conversation as resolved.
Show resolved Hide resolved
{
mParams.SetRemoteNodeId(nodeId);
if (!report.Is<ReadCommissioningInfo2>())
{
ChipLogError(
Controller,
"[BUG] Should read commissioning info (part 2), but report is not ReadCommissioningInfo2. THIS IS A BUG.");
}

ReadCommissioningInfo2 commissioningInfo = report.Get<ReadCommissioningInfo2>();

if (mParams.GetCheckForMatchingFabric())
{
chip::NodeId nodeId = commissioningInfo.nodeId;
yunhanw-google marked this conversation as resolved.
Show resolved Hide resolved
if (nodeId != kUndefinedNodeId)
{
mParams.SetRemoteNodeId(nodeId);
}
}

if (mParams.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore)
{
if (commissioningInfo.isIcd)
{
mNeedIcdRegistraion = true;
ChipLogDetail(Controller, "AutoCommissioner: Device is ICD");
}
}
}
break;
}
Expand Down
2 changes: 2 additions & 0 deletions src/controller/AutoCommissioner.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ class AutoCommissioner : public CommissioningDelegate
ReadCommissioningInfo mDeviceCommissioningInfo;
bool mNeedsDST = false;

bool mNeedIcdRegistraion = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please name this mNeedICDRegistration.


// TODO: Why were the nonces statically allocated, but the certs dynamically allocated?
uint8_t * mDAC = nullptr;
uint16_t mDACLen = 0;
Expand Down
101 changes: 86 additions & 15 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1877,8 +1877,8 @@ void DeviceCommissioner::OnDone(app::ReadClient *)
case CommissioningStage::kReadCommissioningInfo:
ParseCommissioningInfo();
break;
case CommissioningStage::kCheckForMatchingFabric:
ParseFabrics();
case CommissioningStage::kReadCommissioningInfo2:
ParseCommissioningInfo2();
break;
default:
// We're not trying to read anything here, just exit
Expand Down Expand Up @@ -2103,11 +2103,28 @@ void DeviceCommissioner::ParseTimeSyncInfo(ReadCommissioningInfo & info)
}
}

void DeviceCommissioner::ParseFabrics()
void DeviceCommissioner::ParseCommissioningInfo2()
{
ReadCommissioningInfo2 info;
CHIP_ERROR return_err = CHIP_NO_ERROR;

return_err = ParseFabrics(info);

if (return_err == CHIP_NO_ERROR)
{
return_err = ParseICDInfo(info);
}

CommissioningDelegate::CommissioningReport report;
report.Set<ReadCommissioningInfo2>(info);
CommissioningStageComplete(return_err, report);
}

CHIP_ERROR DeviceCommissioner::ParseFabrics(ReadCommissioningInfo2 & info)
{
CHIP_ERROR err;
CHIP_ERROR return_err = CHIP_NO_ERROR;
MatchingFabricInfo info;

// We might not have requested a Fabrics attribute at all, so not having a
// value for it is not an error.
err = mAttributeCache->ForEachAttribute(OperationalCredentials::Id, [this, &info](const app::ConcreteAttributePath & path) {
Expand Down Expand Up @@ -2170,9 +2187,43 @@ void DeviceCommissioner::ParseFabrics()
mPairingDelegate->OnFabricCheck(info.nodeId);
}

CommissioningDelegate::CommissioningReport report;
report.Set<MatchingFabricInfo>(info);
CommissioningStageComplete(return_err, report);
return return_err;
}

CHIP_ERROR DeviceCommissioner::ParseICDInfo(ReadCommissioningInfo2 & info)
{
CHIP_ERROR err;
IcdManagement::Attributes::FeatureMap::TypeInfo::DecodableType featureMap;

err = mAttributeCache->Get<IcdManagement::Attributes::FeatureMap::TypeInfo>(kRootEndpointId, featureMap);
if (err == CHIP_NO_ERROR)
{
info.isIcd = true;
info.checkInProtocolSupport = !!(featureMap & to_underlying(IcdManagement::Feature::kCheckInProtocolSupport));
mkardous-silabs marked this conversation as resolved.
Show resolved Hide resolved
}
else if (err == CHIP_ERROR_KEY_NOT_FOUND)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this would be a case of a non-spec-compliant server that did not respond at all to a non-wildcard path?

Please document exactly why this case is here, because it's not obvious to readers how this situation could happen.

And perhaps this case should just lead comissioning to fail, just like an error other than UnsupportedCluster causes it to fail?

{
info.isIcd = false;
}
else if (err == CHIP_ERROR_IM_STATUS_CODE_RECEIVED)
{
app::StatusIB statusIB;
err = mAttributeCache->GetStatus(
app::ConcreteAttributePath(kRootEndpointId, IcdManagement::Id, IcdManagement::Attributes::FeatureMap::Id), statusIB);
if (err == CHIP_NO_ERROR)
{
if (statusIB.mStatus == Protocols::InteractionModel::Status::UnsupportedCluster)
{
info.isIcd = false;
}
else
{
err = statusIB.ToChipError();
}
}
}

return err;
}

void DeviceCommissioner::OnArmFailSafe(void * context,
Expand Down Expand Up @@ -2414,19 +2465,39 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
SendCommissioningReadRequest(proxy, timeout, readPaths, 9);
}
break;
case CommissioningStage::kCheckForMatchingFabric: {
case CommissioningStage::kReadCommissioningInfo2: {
ChipLogProgress(Controller, "Sending request for commissioning information -- Part 2");

size_t numberOfAttributes = 0;
// This is done in a separate step since we've already used up all the available read paths in the previous read step
app::AttributePathParams readPaths[9];

// Read the current fabrics
if (!params.GetCheckForMatchingFabric())
if (params.GetCheckForMatchingFabric())
{
readPaths[numberOfAttributes++] =
app::AttributePathParams(OperationalCredentials::Id, OperationalCredentials::Attributes::Fabrics::Id);
}

if (params.GetICDRegistrationStrategy() != ICDRegistrationStrategy::kIgnore)
{
readPaths[numberOfAttributes++] =
app::AttributePathParams(endpoint, IcdManagement::Id, IcdManagement::Attributes::FeatureMap::Id);
}

// Current implementation makes sense when we only have a few attributes to read with conditions. Should revisit this if we
// are adding more attributes here.

if (numberOfAttributes == 0)
{
// We don't actually want to do this step, so just bypass it
ChipLogProgress(Controller, "kCheckForMatchingFabric step called without parameter set, skipping");
ChipLogProgress(Controller, "kReadCommissioningInfo2 step called without parameter set, skipping");
CommissioningStageComplete(CHIP_NO_ERROR);
}

// This is done in a separate step since we've already used up all the available read paths in the previous read step
app::AttributePathParams readPaths[1];
readPaths[0] = app::AttributePathParams(OperationalCredentials::Id, OperationalCredentials::Attributes::Fabrics::Id);
SendCommissioningReadRequest(proxy, timeout, readPaths, 1);
else
{
SendCommissioningReadRequest(proxy, timeout, readPaths, numberOfAttributes);
}
}
break;
case CommissioningStage::kConfigureUTCTime: {
Expand Down
5 changes: 4 additions & 1 deletion src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,10 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
#if CHIP_CONFIG_ENABLE_READ_CLIENT
// Parsers for the two different read clients
void ParseCommissioningInfo();
void ParseFabrics();
void ParseCommissioningInfo2();
// Called by ParseCommissioningInfo2
CHIP_ERROR ParseFabrics(ReadCommissioningInfo2 & info);
CHIP_ERROR ParseICDInfo(ReadCommissioningInfo2 & info);
// Called by ParseCommissioningInfo
void ParseTimeSyncInfo(ReadCommissioningInfo & info);
#endif // CHIP_CONFIG_ENABLE_READ_CLIENT
Expand Down
4 changes: 2 additions & 2 deletions src/controller/CommissioningDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ const char * StageToString(CommissioningStage stage)
return "ReadCommissioningInfo";
break;

case kCheckForMatchingFabric:
return "CheckForMatchingFabric";
case kReadCommissioningInfo2:
return "ReadCommissioningInfo2";
break;

case kArmFailsafe:
Expand Down
32 changes: 25 additions & 7 deletions src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ enum CommissioningStage : uint8_t
kError,
kSecurePairing, ///< Establish a PASE session with the device
kReadCommissioningInfo, ///< Query General Commissioning Attributes, Network Features and Time Synchronization Cluster
kCheckForMatchingFabric, ///< Read the current fabrics on the commissionee
kReadCommissioningInfo2, ///< Query ICD state, check for matching fabric
kArmFailsafe, ///< Send ArmFailSafe (0x30:0) command to the device
kConfigRegulatory, ///< Send SetRegulatoryConfig (0x30:2) command to the device
kConfigureUTCTime, ///< SetUTCTime if the DUT has a time cluster
Expand Down Expand Up @@ -71,6 +71,13 @@ enum CommissioningStage : uint8_t
kNeedsNetworkCreds,
};

enum ICDRegistrationStrategy : uint8_t
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not an enum class?

{
kIgnore, ///< Do not check whether the device is an ICD during commissioning
kBeforeComplete, ///< Do commissioner self-registration or external controller registration,
yunhanw-google marked this conversation as resolved.
Show resolved Hide resolved
///< Controller should provide a ICDKey manager for generating symmetric key
};

const char * StageToString(CommissioningStage stage);

struct WiFiCredentials
Expand Down Expand Up @@ -493,6 +500,13 @@ class CommissioningParameters
return *this;
}

ICDRegistrationStrategy GetICDRegistrationStrategy() const { return mICDRegistrationStrategy; }
CommissioningParameters & SetICDRegistrationStrategy(ICDRegistrationStrategy icdRegistrationStrategy)
{
mICDRegistrationStrategy = icdRegistrationStrategy;
return *this;
}

// Clear all members that depend on some sort of external buffer. Can be
// used to make sure that we are not holding any dangling pointers.
void ClearExternalBufferDependentValues()
Expand Down Expand Up @@ -550,7 +564,8 @@ class CommissioningParameters
Optional<bool> mAttemptWiFiNetworkScan;
Optional<bool> mAttemptThreadNetworkScan; // This automatically gets set to false when a ThreadOperationalDataset is set
Optional<bool> mSkipCommissioningComplete;
bool mCheckForMatchingFabric = false;
ICDRegistrationStrategy mICDRegistrationStrategy = ICDRegistrationStrategy::kIgnore;
bool mCheckForMatchingFabric = false;
};

struct RequestedCertificate
Expand Down Expand Up @@ -632,9 +647,12 @@ struct ReadCommissioningInfo
uint8_t maxTimeZoneSize = 1;
uint8_t maxDSTSize = 1;
};
struct MatchingFabricInfo

struct ReadCommissioningInfo2
{
NodeId nodeId = kUndefinedNodeId;
NodeId nodeId = kUndefinedNodeId;
bool isIcd = false;
bool checkInProtocolSupport = false;
};

struct TimeZoneResponseInfo
Expand Down Expand Up @@ -668,7 +686,7 @@ class CommissioningDelegate
virtual ~CommissioningDelegate(){};
/* CommissioningReport is returned after each commissioning step is completed. The reports for each step are:
* kReadCommissioningInfo - ReadCommissioningInfo
* kCheckForMatchingFabric = MatchingFabricInfo
* kReadCommissioningInfo2: ReadCommissioningInfo2
* kArmFailsafe: CommissioningErrorInfo if there is an error
* kConfigRegulatory: CommissioningErrorInfo if there is an error
* kConfigureUTCTime: None
Expand All @@ -693,8 +711,8 @@ class CommissioningDelegate
* kCleanup: none
*/
struct CommissioningReport : Variant<RequestedCertificate, AttestationResponse, CSRResponse, NocChain, OperationalNodeFoundData,
ReadCommissioningInfo, AttestationErrorInfo, CommissioningErrorInfo,
NetworkCommissioningStatusInfo, MatchingFabricInfo, TimeZoneResponseInfo>
ReadCommissioningInfo, ReadCommissioningInfo2, AttestationErrorInfo,
CommissioningErrorInfo, NetworkCommissioningStatusInfo, TimeZoneResponseInfo>
{
CommissioningReport() : stageCompleted(CommissioningStage::kError) {}
CommissioningStage stageCompleted;
Expand Down
2 changes: 0 additions & 2 deletions src/controller/python/OpCredsBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,6 @@ class TestCommissioner : public chip::Controller::AutoCommissioner
{
switch (stage)
{
case chip::Controller::CommissioningStage::kCheckForMatchingFabric:
return mParams.GetCheckForMatchingFabric();
case chip::Controller::CommissioningStage::kWiFiNetworkEnable:
case chip::Controller::CommissioningStage::kFailsafeBeforeWiFiEnable:
case chip::Controller::CommissioningStage::kWiFiNetworkSetup:
Expand Down
Loading