Skip to content

Commit

Permalink
[OTA] Use CASESessionManager to establish CASE sessions from OTAReque…
Browse files Browse the repository at this point in the history
…stor class (#12636)
  • Loading branch information
carol-apple authored Dec 9, 2021
1 parent 6ea2c90 commit f18d8b7
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 264 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,23 @@

#include <app-common/zap-generated/cluster-objects.h>
#include <app/clusters/ota-provider/ota-provider-delegate.h>
#include <app/server/Server.h>
#include <app/util/af.h>
#include <credentials/FabricTable.h>
#include <crypto/RandUtils.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/CHIPMemString.h>
#include <protocols/secure_channel/PASESession.h> // For chip::kTestDeviceNodeId

#include <string.h>

using chip::ByteSpan;
using chip::CharSpan;
using chip::FabricIndex;
using chip::FabricInfo;
using chip::MutableCharSpan;
using chip::NodeId;
using chip::Optional;
using chip::Server;
using chip::Span;
using chip::app::Clusters::OTAProviderDelegate;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;
Expand All @@ -57,19 +63,18 @@ void GenerateUpdateToken(uint8_t * buf, size_t bufSize)
}
}

bool GenerateBdxUri(const Span<char> & fileDesignator, Span<char> outUri, size_t availableSize)
bool GenerateBdxUri(NodeId nodeId, CharSpan fileDesignator, MutableCharSpan outUri)
{
static constexpr char bdxPrefix[] = "bdx://";
chip::NodeId nodeId = chip::kTestDeviceNodeId; // TODO: read this dynamically
size_t nodeIdHexStrLen = sizeof(nodeId) * 2;
size_t expectedLength = strlen(bdxPrefix) + nodeIdHexStrLen + 1 + fileDesignator.size();

if (expectedLength >= availableSize)
if (expectedLength >= outUri.size())
{
return false;
}

size_t written = static_cast<size_t>(snprintf(outUri.data(), availableSize, "%s" ChipLogFormatX64 "/%s", bdxPrefix,
size_t written = static_cast<size_t>(snprintf(outUri.data(), outUri.size(), "%s" ChipLogFormatX64 "/%s", bdxPrefix,
ChipLogValueX64(nodeId), fileDesignator.data()));

return expectedLength == written;
Expand Down Expand Up @@ -115,9 +120,16 @@ EmberAfStatus OTAProviderExample::HandleQueryImage(chip::app::CommandHandler * c

if (strlen(mOTAFilePath))
{
// TODO: This uses the current node as the provider to supply the OTA image. This can be configurable such that the provider
// supplying the response is not the provider supplying the OTA image.
FabricIndex fabricIndex = commandObj->GetExchangeContext()->GetSessionHandle().GetFabricIndex();
FabricInfo * fabricInfo = Server::GetInstance().GetFabricTable().FindFabricWithIndex(fabricIndex);
NodeId nodeId = fabricInfo->GetPeerId().GetNodeId();

// Only doing BDX transport for now
GenerateBdxUri(Span<char>(mOTAFilePath, strlen(mOTAFilePath)), Span<char>(uriBuf, 0), kUriMaxLen);
ChipLogDetail(SoftwareUpdate, "generated URI: %s", uriBuf);
MutableCharSpan uri(uriBuf, kUriMaxLen);
GenerateBdxUri(nodeId, CharSpan(mOTAFilePath, strlen(mOTAFilePath)), uri);
ChipLogDetail(SoftwareUpdate, "Generated URI: %.*s", static_cast<int>(uri.size()), uri.data());
}

// Set Status for the Query Image Response
Expand Down
61 changes: 29 additions & 32 deletions examples/ota-requestor-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
* limitations under the License.
*/

#include <app/OperationalDeviceProxy.h>
#include <app/CASEClientPool.h>
#include <app/OperationalDeviceProxyPool.h>
#include <app/server/Server.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
Expand All @@ -30,30 +31,38 @@

using chip::BDXDownloader;
using chip::ByteSpan;
using chip::CASEClientPool;
using chip::CharSpan;
using chip::DeviceProxy;
using chip::EndpointId;
using chip::FabricIndex;
using chip::GetRequestorInstance;
using chip::LinuxOTAImageProcessor;
using chip::NodeId;
using chip::OnDeviceConnected;
using chip::OnDeviceConnectionFailure;
using chip::OperationalDeviceProxyPool;
using chip::OTADownloader;
using chip::OTAImageProcessorParams;
using chip::OTARequestor;
using chip::PeerId;
using chip::Server;
using chip::VendorId;
using chip::Callback::Callback;
using chip::Inet::IPAddress;
using chip::System::Layer;
using chip::Transport::PeerAddress;
using namespace chip::ArgParser;
using namespace chip::Messaging;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;

OTARequestor requestorCore;
LinuxOTARequestorDriver requestorUser;
BDXDownloader downloader;
LinuxOTAImageProcessor imageProcessor;
constexpr size_t kMaxActiveCaseClients = 2;
constexpr size_t kMaxActiveDevices = 8;

OTARequestor gRequestorCore;
LinuxOTARequestorDriver gRequestorUser;
BDXDownloader gDownloader;
LinuxOTAImageProcessor gImageProcessor;
CASEClientPool<kMaxActiveCaseClients> gCASEClientPool;
OperationalDeviceProxyPool<kMaxActiveDevices> gDevicePool;

bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier, const char * aName, const char * aValue);
void OnStartDelayTimerHandler(Layer * systemLayer, void * appState);
Expand All @@ -62,10 +71,8 @@ constexpr uint16_t kOptionProviderNodeId = 'n';
constexpr uint16_t kOptionProviderFabricIndex = 'f';
constexpr uint16_t kOptionUdpPort = 'u';
constexpr uint16_t kOptionDiscriminator = 'd';
constexpr uint16_t kOptionIPAddress = 'i';
constexpr uint16_t kOptionDelayQuery = 'q';

const char * ipAddress = NULL;
NodeId providerNodeId = 0x0;
FabricIndex providerFabricIndex = 1;
uint16_t requestorSecurePort = 0;
Expand All @@ -77,8 +84,6 @@ OptionDef cmdLineOptionsDef[] = {
{ "providerFabricIndex", chip::ArgParser::kArgumentRequired, kOptionProviderFabricIndex },
{ "udpPort", chip::ArgParser::kArgumentRequired, kOptionUdpPort },
{ "discriminator", chip::ArgParser::kArgumentRequired, kOptionDiscriminator },
// TODO: This can be removed once OperationalDeviceProxy can resolve the IP Address from Node ID
{ "ipaddress", chip::ArgParser::kArgumentRequired, kOptionIPAddress },
{ "delayQuery", chip::ArgParser::kArgumentRequired, kOptionDelayQuery },
{},
};
Expand All @@ -95,8 +100,6 @@ OptionSet cmdLineOptions = { HandleOptions, cmdLineOptionsDef, "PROGRAM OPTIONS"
" -d/--discriminator <discriminator>\n"
" A 12-bit value used to discern between multiple commissionable CHIP device\n"
" advertisements. If none is specified, default value is 3840.\n"
" -i/--ipaddress <IP Address>\n"
" The IP Address of the OTA Provider to connect to. This value must be supplied.\n"
" -q/--delayQuery <Time in seconds>\n"
" From boot up, the amount of time to wait before triggering the QueryImage\n"
" command. If none or zero is supplied, QueryImage will not be triggered.\n" };
Expand Down Expand Up @@ -144,10 +147,6 @@ bool HandleOptions(const char * aProgram, OptionSet * aOptions, int aIdentifier,
retval = false;
}
break;
case kOptionIPAddress:
ipAddress = aValue;
ChipLogError(SoftwareUpdate, "IP Address = %s", aValue);
break;
case kOptionDelayQuery:
delayQueryTimeInSec = static_cast<uint16_t>(strtol(aValue, NULL, 0));
break;
Expand Down Expand Up @@ -201,39 +200,37 @@ int main(int argc, char * argv[])
SetDeviceAttestationCredentialsProvider(chip::Credentials::Examples::GetExampleDACProvider());

// Initialize and interconnect the Requestor and Image Processor objects -- START
SetRequestorInstance(&requestorCore);
SetRequestorInstance(&gRequestorCore);

// Set server instance used for session establishment
chip::Server * server = &(chip::Server::GetInstance());
server->SetCASEClientPool(&gCASEClientPool);
server->SetDevicePool(&gDevicePool);
gRequestorCore.SetServerInstance(server);

// Connect the Requestor and Requestor Driver objects
requestorCore.SetOtaRequestorDriver(&requestorUser);
gRequestorCore.SetOtaRequestorDriver(&gRequestorUser);

// WARNING: this is probably not realistic to know such details of the image or to even have an OTADownloader instantiated at
// the beginning of program execution. We're using hardcoded values here for now since this is a reference application.
// TODO: instatiate and initialize these values when QueryImageResponse tells us an image is available
// TODO: add API for OTARequestor to pass QueryImageResponse info to the application to use for OTADownloader init
OTAImageProcessorParams ipParams;
ipParams.imageFile = CharSpan("test.txt");
imageProcessor.SetOTAImageProcessorParams(ipParams);
imageProcessor.SetOTADownloader(&downloader);
gImageProcessor.SetOTAImageProcessorParams(ipParams);
gImageProcessor.SetOTADownloader(&gDownloader);

// Connect the Downloader and Image Processor objects
downloader.SetImageProcessorDelegate(&imageProcessor);
gDownloader.SetImageProcessorDelegate(&gImageProcessor);

requestorCore.SetBDXDownloader(&downloader);
gRequestorCore.SetBDXDownloader(&gDownloader);
// Initialize and interconnect the Requestor and Image Processor objects -- END

// Pass the IP Address to the OTARequestor object: Use of explicit IP address is temporary
// until the Exchange Layer implements address resolution
{
IPAddress ipAddr;
IPAddress::FromString(ipAddress, ipAddr);
requestorCore.SetIpAddress(ipAddr);
}

// Test Mode operation: If a delay is provided, QueryImage after the timer expires
if (delayQueryTimeInSec > 0)
{
// In this mode Provider node ID and fabric idx must be supplied explicitly from program args
requestorCore.TestModeSetProviderParameters(providerNodeId, providerFabricIndex);
gRequestorCore.TestModeSetProviderParameters(providerNodeId, providerFabricIndex);

chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(delayQueryTimeInSec * 1000),
OnStartDelayTimerHandler, nullptr);
Expand Down
3 changes: 3 additions & 0 deletions src/app/CASESessionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class CASESessionManager : public SessionReleaseDelegate, public Dnssd::Resolver
VerifyOrDie(params.sessionInitParams.Validate() == CHIP_NO_ERROR);

mConfig = params;

// TODO: Revisit who should be set as the resolver delegate
Dnssd::Resolver::Instance().SetResolverDelegate(this);
}

virtual ~CASESessionManager() {}
Expand Down
61 changes: 61 additions & 0 deletions src/app/clusters/ota-requestor/BDXDownloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "BDXDownloader.h"

#include <lib/core/CHIPError.h>
#include <lib/support/BufferReader.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CodeUtils.h>
#include <protocols/bdx/BdxMessages.h>
#include <system/SystemClock.h> /* TODO:(#12520) remove */
Expand Down Expand Up @@ -215,4 +217,63 @@ CHIP_ERROR BDXDownloader::HandleBdxEvent(const chip::bdx::TransferSession::Outpu
return CHIP_NO_ERROR;
}

// TODO: Add unit tests for parsing BDX URI
CHIP_ERROR BDXDownloader::ParseBdxUri(CharSpan uri, NodeId & nodeId, MutableCharSpan fileDesignator)
{
// Check against minimum length of a valid BDX URI
if (uri.size() < kValidBdxUriMinLen)
{
return CHIP_ERROR_INVALID_STRING_LENGTH;
}

uint8_t readValue[kUriMaxLen];
Encoding::LittleEndian::Reader uriReader(reinterpret_cast<const uint8_t *>(uri.data()), uri.size());

// Check the scheme field matches the BDX prefix
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, sizeof(bdxPrefix)).StatusCode());
ByteSpan expectedScheme(bdxPrefix, sizeof(bdxPrefix));
ByteSpan actualScheme(readValue, sizeof(bdxPrefix));
if (!expectedScheme.data_equal(actualScheme))
{
return CHIP_ERROR_INVALID_SCHEME_PREFIX;
}

// Extract the node ID from the authority field
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, kNodeIdHexStringLen).StatusCode());
uint8_t buffer[kNodeIdHexStringLen];
if (Encoding::HexToBytes(reinterpret_cast<const char *>(readValue), kNodeIdHexStringLen, buffer, kNodeIdHexStringLen) == 0)
{
return CHIP_ERROR_INVALID_DESTINATION_NODE_ID;
}
nodeId = Encoding::BigEndian::Get64(buffer);
if (!IsOperationalNodeId(nodeId))
{
return CHIP_ERROR_INVALID_DESTINATION_NODE_ID;
}

// Verify the separator between authority and path fields
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, sizeof(bdxSeparator)).StatusCode());
ByteSpan expectedSeparator(bdxSeparator, sizeof(bdxSeparator));
ByteSpan actualSeparator(readValue, sizeof(bdxSeparator));
if (!expectedSeparator.data_equal(actualSeparator))
{
return CHIP_ERROR_MISSING_URI_SEPARATOR;
}

// Extract file designator from the path field
size_t fileDesignatorLength = uriReader.Remaining();
memset(readValue, 0, sizeof(readValue));
ReturnErrorOnFailure(uriReader.ReadBytes(readValue, fileDesignatorLength).StatusCode());
size_t written = static_cast<size_t>(snprintf(fileDesignator.data(), fileDesignator.size(), "%s", readValue));
if (written != fileDesignatorLength)
{
return CHIP_ERROR_INTERNAL;
}

return CHIP_NO_ERROR;
}

} // namespace chip
12 changes: 12 additions & 0 deletions src/app/clusters/ota-requestor/BDXDownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@

namespace chip {

// Constants for BDX URI parsing
constexpr uint8_t bdxPrefix[] = { 'b', 'd', 'x', ':', '/', '/' };
constexpr uint8_t bdxSeparator[] = { '/' };
constexpr uint8_t kValidBdxUriMinLen = 24;
constexpr uint8_t kNodeIdHexStringLen = 16;
constexpr size_t kUriMaxLen = 256;

class BDXDownloader : public chip::OTADownloader
{
public:
Expand Down Expand Up @@ -65,6 +72,11 @@ class BDXDownloader : public chip::OTADownloader
CHIP_ERROR FetchNextData() override;
// TODO: override SkipData

/**
* Validate the URI and parse the BDX URI for various fields
*/
CHIP_ERROR ParseBdxUri(CharSpan uri, NodeId & nodeId, MutableCharSpan fileDesignator);

private:
void PollTransferSession();
CHIP_ERROR HandleBdxEvent(const chip::bdx::TransferSession::OutputEvent & outEvent);
Expand Down
2 changes: 1 addition & 1 deletion src/app/clusters/ota-requestor/ClusterInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ bool emberAfOtaSoftwareUpdateRequestorClusterAnnounceOtaProviderCallback(
const chip::app::Clusters::OtaSoftwareUpdateRequestor::Commands::AnnounceOtaProvider::DecodableType & commandData)
{
EmberAfStatus status;
OTARequestorInterface * requestor = GetRequestorInstance();
chip::OTARequestorInterface * requestor = chip::GetRequestorInstance();

if (requestor != nullptr)
{
Expand Down
Loading

0 comments on commit f18d8b7

Please sign in to comment.