Skip to content

Commit

Permalink
Check feature map for network technology
Browse files Browse the repository at this point in the history
Auto commissioner will check the feature map if we're commissioning
using BLE, and will check that the supplied arguments match the
feature map.

This will let vendors proactively just supply both wifi and thread
credentials and the commissioner will do the right thing.
  • Loading branch information
cecille committed Jan 28, 2022
1 parent 92f8343 commit 1732764
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 14 deletions.
73 changes: 61 additions & 12 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam
return CHIP_NO_ERROR;
}

CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR lastErr)
CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR & lastErr)
{
if (lastErr != CHIP_NO_ERROR)
{
Expand All @@ -107,6 +107,15 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
case CommissioningStage::kSecurePairing:
return CommissioningStage::kArmFailsafe;
case CommissioningStage::kArmFailsafe:
if (mNeedsNetworkSetup)
{
return CommissioningStage::kGetNetworkTechnology;
}
else
{
return CommissioningStage::kConfigRegulatory;
}
case CommissioningStage::kGetNetworkTechnology:
return CommissioningStage::kConfigRegulatory;
case CommissioningStage::kConfigRegulatory:
return CommissioningStage::kSendPAICertificateRequest;
Expand All @@ -128,24 +137,35 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
// TODO(cecille): device attestation casues operational cert provisioinging to happen, This should be a separate stage.
// For thread and wifi, this should go to network setup then enable. For on-network we can skip right to finding the
// operational network because the provisioning of certificates will trigger the device to start operational advertising.
if (mParams.GetWiFiCredentials().HasValue())
{
return CommissioningStage::kWiFiNetworkSetup;
}
else if (mParams.GetThreadOperationalDataset().HasValue())
if (mNeedsNetworkSetup)
{
return CommissioningStage::kThreadNetworkSetup;
if (mParams.GetWiFiCredentials().HasValue() && mNetworkTechnology.Has(NetworkClusterFeatureFlags::kWifi))
{
return CommissioningStage::kWiFiNetworkSetup;
}
else if (mParams.GetThreadOperationalDataset().HasValue() &&
mNetworkTechnology.Has(NetworkClusterFeatureFlags::kThread))
{
return CommissioningStage::kThreadNetworkSetup;
}
else
{
ChipLogError(Controller, "Required network information not provided in commissioning parameters");
lastErr = CHIP_ERROR_INVALID_ARGUMENT;
return CommissioningStage::kCleanup;
}
}
else
{
// TODO: I dont think this is to spec - not sure where we'd have a commissioner that doesn't have dnssd.
#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
return CommissioningStage::kFindOperational;
#else
return CommissioningStage::kSendComplete;
#endif
}
case CommissioningStage::kWiFiNetworkSetup:
if (mParams.GetThreadOperationalDataset().HasValue())
if (mParams.GetThreadOperationalDataset().HasValue() && mNetworkTechnology.Has(NetworkClusterFeatureFlags::kThread))
{
return CommissioningStage::kThreadNetworkSetup;
}
Expand All @@ -154,7 +174,7 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
return CommissioningStage::kWiFiNetworkEnable;
}
case CommissioningStage::kThreadNetworkSetup:
if (mParams.GetWiFiCredentials().HasValue())
if (mParams.GetWiFiCredentials().HasValue() && mNetworkTechnology.Has(NetworkClusterFeatureFlags::kWifi))
{
return CommissioningStage::kWiFiNetworkEnable;
}
Expand All @@ -164,7 +184,7 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
}

case CommissioningStage::kWiFiNetworkEnable:
if (mParams.GetThreadOperationalDataset().HasValue())
if (mParams.GetThreadOperationalDataset().HasValue() && mNetworkTechnology.Has(NetworkClusterFeatureFlags::kThread))
{
return CommissioningStage::kThreadNetworkEnable;
}
Expand All @@ -173,7 +193,12 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
return CommissioningStage::kFindOperational;
}
case CommissioningStage::kThreadNetworkEnable:
// TODO: I dont think this is to spec - not sure where we'd have a commissioner that doesn't have dnssd.
#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
return CommissioningStage::kFindOperational;
#else
return CommissioningStage::kSendComplete;
#endif
case CommissioningStage::kFindOperational:
return CommissioningStage::kSendComplete;
case CommissioningStage::kSendComplete:
Expand All @@ -193,9 +218,19 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
void AutoCommissioner::StartCommissioning(CommissioneeDeviceProxy * proxy)
{
// TODO: check that there is no commissioning in progress currently.

if (proxy == nullptr || !proxy->GetSecureSession().HasValue())
{
ChipLogError(Controller, "Device proxy secure session error");
return;
}
mCommissioneeDeviceProxy = proxy;
mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, CommissioningStage::kArmFailsafe, mParams, this, 0,
GetCommandTimeout(CommissioningStage::kArmFailsafe));
mNeedsNetworkSetup =
mCommissioneeDeviceProxy->GetSecureSession().Value()->AsSecureSession()->GetPeerAddress().GetTransportType() ==
Transport::Type::kBle;
CHIP_ERROR err = CHIP_NO_ERROR;
CommissioningStage nextStage = GetNextCommissioningStage(CommissioningStage::kSecurePairing, err);
mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, 0, GetCommandTimeout(nextStage));
}

Optional<System::Clock::Timeout> AutoCommissioner::GetCommandTimeout(CommissioningStage stage)
Expand Down Expand Up @@ -254,6 +289,20 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
{
switch (report.stageCompleted)
{
case CommissioningStage::kGetNetworkTechnology:
mNetworkTechnology.SetRaw(report.Get<FeatureMap>().features);
// Only one of these features can be set at a time.
if (!mNetworkTechnology.HasOnly(NetworkClusterFeatureFlags::kWifi) &&
!mNetworkTechnology.HasOnly(NetworkClusterFeatureFlags::kThread) &&
mNetworkTechnology.HasOnly(NetworkClusterFeatureFlags::kEthernet))
{
ChipLogError(Controller,
"Network Commissioning cluster is malformed - more than one network technology is specified (0x%x)",
report.Get<FeatureMap>().features);
err = CHIP_ERROR_INTEGRITY_CHECK_FAILED;
break;
}
break;
case CommissioningStage::kSendPAICertificateRequest:
SetPAI(report.Get<RequestedCertificate>().certificate);
break;
Expand Down
13 changes: 12 additions & 1 deletion src/controller/AutoCommissioner.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ namespace Controller {

class DeviceCommissioner;

// It seems like this should come from the zap
enum NetworkClusterFeatureFlags : uint32_t
{
kWifi = 0x01,
kThread = 0x02,
kEthernet = 0x04,
};

class AutoCommissioner : public CommissioningDelegate
{
public:
Expand All @@ -39,7 +47,7 @@ class AutoCommissioner : public CommissioningDelegate
CHIP_ERROR CommissioningStepFinished(CHIP_ERROR err, CommissioningDelegate::CommissioningReport report) override;

private:
CommissioningStage GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR lastErr);
CommissioningStage GetNextCommissioningStage(CommissioningStage currentStage, CHIP_ERROR & lastErr);
void ReleaseDAC();
void ReleasePAI();

Expand All @@ -61,6 +69,9 @@ class AutoCommissioner : public CommissioningDelegate
uint8_t mSsid[CommissioningParameters::kMaxSsidLen];
uint8_t mCredentials[CommissioningParameters::kMaxCredentialsLen];
uint8_t mThreadOperationalDataset[CommissioningParameters::kMaxThreadDatasetLen];
// TODO: if the device library adds a network commissioning device type, this will need to be 1 per endpoint.
BitFlags<NetworkClusterFeatureFlags> mNetworkTechnology;
bool mNeedsNetworkSetup = false;

// TODO: Why were the nonces statically allocated, but the certs dynamically allocated?
uint8_t * mDAC = nullptr;
Expand Down
23 changes: 23 additions & 0 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1617,6 +1617,20 @@ void DeviceCommissioner::SetupCluster(ClusterBase & base, DeviceProxy * proxy, E
base.SetCommandTimeout(timeout);
}

void OnFeatureMapSuccess(void * context, uint32_t value)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
CommissioningDelegate::CommissioningReport report;
report.Set<FeatureMap>(value);
commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report);
}

void AttributeReadFailure(void * context, CHIP_ERROR status)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
commissioner->CommissioningStageComplete(status);
}

void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, CommissioningStage step, CommissioningParameters & params,
CommissioningDelegate * delegate, EndpointId endpoint,
Optional<System::Clock::Timeout> timeout)
Expand Down Expand Up @@ -1644,6 +1658,15 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
genCom.ArmFailSafe(mSuccess.Cancel(), mFailure.Cancel(), params.GetFailsafeTimerSeconds(), breadcrumb, kCommandTimeoutMs);
}
break;
case CommissioningStage::kGetNetworkTechnology: {
ChipLogProgress(Controller, "Sending request for network cluster feature map");
NetworkCommissioningCluster netCom;
// TODO: swap to givin endpoint once that PR is merged
netCom.Associate(proxy, 0);
netCom.ReadAttribute<app::Clusters::NetworkCommissioning::Attributes::FeatureMap::TypeInfo>(this, OnFeatureMapSuccess,
AttributeReadFailure);
}
break;
case CommissioningStage::kConfigRegulatory: {
// To set during config phase:
// UTC time
Expand Down
6 changes: 6 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,12 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
const ByteSpan & attestationNonce, const ByteSpan & pai, const ByteSpan & dac,
DeviceProxy * proxy);

/**
* @brief
* Sends CommissioningStepComplete report to the commissioning delegate. Function will fill in current step.
* @params[in] err error from the current step
* @params[in] report report to send. Current step will be filled in automatically
*/
void
CommissioningStageComplete(CHIP_ERROR err,
CommissioningDelegate::CommissioningReport report = CommissioningDelegate::CommissioningReport());
Expand Down
9 changes: 8 additions & 1 deletion src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum CommissioningStage : uint8_t
// kConfigTime, // NOT YET IMPLEMENTED
// kConfigTimeZone, // NOT YET IMPLEMENTED
// kConfigDST, // NOT YET IMPLEMENTED
kGetNetworkTechnology,
kConfigRegulatory,
kSendPAICertificateRequest,
kSendDACCertificateRequest,
Expand Down Expand Up @@ -232,12 +233,18 @@ struct OperationalNodeFoundData
OperationalNodeFoundData(OperationalDeviceProxy * proxy) : operationalProxy(proxy) {}
OperationalDeviceProxy * operationalProxy;
};
struct FeatureMap
{
FeatureMap(uint32_t featureBitmap) : features(featureBitmap) {}
uint32_t features;
};

class CommissioningDelegate
{
public:
virtual ~CommissioningDelegate(){};

struct CommissioningReport : Variant<RequestedCertificate, AttestationResponse, NocChain, OperationalNodeFoundData>
struct CommissioningReport : Variant<RequestedCertificate, AttestationResponse, NocChain, OperationalNodeFoundData, FeatureMap>
{
CommissioningReport() : stageCompleted(CommissioningStage::kError) {}
CommissioningStage stageCompleted;
Expand Down

0 comments on commit 1732764

Please sign in to comment.