blob: c56a92453c94f035ac03e56c558cbc75fdbee611 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 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.
*/
/**
* @file
* This file contains definitions for Device class. The objects of this
* class will be used by Controller applications to interact with CHIP
* devices. The class provides mechanism to construct, send and receive
* messages to and from the corresponding CHIP devices.
*/
#pragma once
#include <app/CASEClient.h>
#include <app/CASEClientPool.h>
#include <app/DeviceProxy.h>
#include <app/util/attribute-filter.h>
#include <app/util/basic-types.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeDelegate.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/Flags.h>
#include <protocols/secure_channel/CASESession.h>
#include <protocols/secure_channel/SessionIDAllocator.h>
#include <system/SystemLayer.h>
#include <transport/SessionManager.h>
#include <transport/TransportMgr.h>
#include <transport/raw/MessageHeader.h>
#include <transport/raw/UDP.h>
#include <lib/dnssd/ResolverProxy.h>
namespace chip {
struct DeviceProxyInitParams
{
SessionManager * sessionManager = nullptr;
Messaging::ExchangeManager * exchangeMgr = nullptr;
SessionIDAllocator * idAllocator = nullptr;
FabricTable * fabricTable = nullptr;
CASEClientPoolDelegate * clientPool = nullptr;
Controller::DeviceControllerInteractionModelDelegate * imDelegate = nullptr;
Optional<ReliableMessageProtocolConfig> mrpLocalConfig = Optional<ReliableMessageProtocolConfig>::Missing();
CHIP_ERROR Validate() const
{
ReturnErrorCodeIf(sessionManager == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(exchangeMgr == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(idAllocator == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(fabricTable == nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(clientPool == nullptr, CHIP_ERROR_INCORRECT_STATE);
return CHIP_NO_ERROR;
}
};
class OperationalDeviceProxy;
typedef void (*OnDeviceConnected)(void * context, OperationalDeviceProxy * device);
typedef void (*OnDeviceConnectionFailure)(void * context, PeerId peerId, CHIP_ERROR error);
class DLL_EXPORT OperationalDeviceProxy : public DeviceProxy, SessionReleaseDelegate, public SessionEstablishmentDelegate
{
public:
virtual ~OperationalDeviceProxy();
OperationalDeviceProxy(DeviceProxyInitParams & params, PeerId peerId)
{
VerifyOrReturn(params.Validate() == CHIP_NO_ERROR);
mSystemLayer = params.exchangeMgr->GetSessionManager()->SystemLayer();
mInitParams = params;
mPeerId = peerId;
mFabricInfo = params.fabricTable->FindFabricWithCompressedId(peerId.GetCompressedFabricId());
mState = State::NeedsAddress;
}
OperationalDeviceProxy(DeviceProxyInitParams & params, PeerId peerId, const Dnssd::ResolvedNodeData & nodeResolutionData) :
OperationalDeviceProxy(params, peerId)
{
OnNodeIdResolved(nodeResolutionData);
}
void Clear();
/*
* This function can be called to establish a secure session with the device.
*
* The device is expected to have been commissioned, A CASE session
* setup will be triggered.
*
* On establishing the session, the callback function `onConnection` will be called. If the
* session setup fails, `onFailure` will be called.
*
* If the session already exists, `onConnection` will be called immediately.
* If the resolver is null and the device state is State::NeedsAddress, CHIP_ERROR_INVALID_ARGUMENT will be
* returned.
*/
CHIP_ERROR Connect(Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure, Dnssd::ResolverProxy * resolver);
bool IsConnected() const { return mState == State::SecureConnected; }
bool IsConnecting() const { return mState == State::Connecting; }
/**
* Called when a connection is closing.
* The object releases all resources associated with the connection.
*/
void OnSessionReleased(const SessionHandle & session) override;
void OnNodeIdResolved(const Dnssd::ResolvedNodeData & nodeResolutionData)
{
mDeviceAddress = ToPeerAddress(nodeResolutionData);
mMRPConfig = nodeResolutionData.GetMRPConfig();
if (mState == State::NeedsAddress)
{
mState = State::Initialized;
}
}
/**
* Mark any open session with the device as expired.
*/
CHIP_ERROR Disconnect() override;
/**
* Use SetConnectedSession if 'this' object is a newly allocated device proxy.
* It will take an existing session, such as the one established
* during commissioning, and use it for this device proxy.
*
* Note: Avoid using this function generally as it is Deprecated
*/
void SetConnectedSession(SessionHandle handle);
NodeId GetDeviceId() const override { return mPeerId.GetNodeId(); }
/**
* Update data of the device.
* This function will set new IP address, port and MRP retransmission intervals of the device.
* Since the device settings might have been moved from RAM to the persistent storage, the function
* will load the device settings first, before making the changes.
*/
CHIP_ERROR UpdateDeviceData(const Transport::PeerAddress & addr, const ReliableMessageProtocolConfig & config);
PeerId GetPeerId() const { return mPeerId; }
bool MatchesSession(const SessionHandle & session) const { return mSecureSession.Contains(session); }
uint8_t GetNextSequenceNumber() override { return mSequenceNumber++; };
CHIP_ERROR ShutdownSubscriptions() override;
Controller::DeviceControllerInteractionModelDelegate * GetInteractionModelDelegate() override { return mInitParams.imDelegate; }
Messaging::ExchangeManager * GetExchangeManager() const override { return mInitParams.exchangeMgr; }
chip::Optional<SessionHandle> GetSecureSession() const override { return mSecureSession.ToOptional(); }
bool GetAddress(Inet::IPAddress & addr, uint16_t & port) const override;
Transport::PeerAddress GetPeerAddress() const { return mDeviceAddress; }
static Transport::PeerAddress ToPeerAddress(const Dnssd::ResolvedNodeData & nodeData)
{
Inet::InterfaceId interfaceId = Inet::InterfaceId::Null();
// TODO - Revisit usage of InterfaceID only for addresses that are IPv6 LLA
// Only use the DNS-SD resolution's InterfaceID for addresses that are IPv6 LLA.
// For all other addresses, we should rely on the device's routing table to route messages sent.
// Forcing messages down an InterfaceId might fail. For example, in bridged networks like Thread,
// mDNS advertisements are not usually received on the same interface the peer is reachable on.
if (nodeData.mAddress[0].IsIPv6LinkLocal())
{
interfaceId = nodeData.mInterfaceId;
}
return Transport::PeerAddress::UDP(nodeData.mAddress[0], nodeData.mPort, interfaceId);
}
private:
enum class State
{
Uninitialized,
NeedsAddress,
Initialized,
Connecting,
SecureConnected,
};
DeviceProxyInitParams mInitParams;
FabricInfo * mFabricInfo;
System::Layer * mSystemLayer;
CASEClient * mCASEClient = nullptr;
PeerId mPeerId;
Transport::PeerAddress mDeviceAddress = Transport::PeerAddress::UDP(Inet::IPAddress::Any);
State mState = State::Uninitialized;
SessionHolder mSecureSession;
uint8_t mSequenceNumber = 0;
Callback::CallbackDeque mConnectionSuccess;
Callback::CallbackDeque mConnectionFailure;
CHIP_ERROR EstablishConnection();
bool IsSecureConnected() const override { return mState == State::SecureConnected; }
static void HandleCASEConnected(void * context, CASEClient * client);
static void HandleCASEConnectionFailure(void * context, CASEClient * client, CHIP_ERROR error);
static void CloseCASESessionTask(System::Layer * layer, void * context);
void DeferCloseCASESession();
void EnqueueConnectionCallbacks(Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure);
void DequeueConnectionSuccessCallbacks(bool executeCallback);
void DequeueConnectionFailureCallbacks(CHIP_ERROR error, bool executeCallback);
};
} // namespace chip