LCOV - code coverage report
Current view: top level - app - OperationalSessionSetup.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 1 36 2.8 %
Date: 2024-02-15 08:20:41 Functions: 1 15 6.7 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020-2021 Project CHIP Authors
       4             :  *    All rights reserved.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : /**
      20             :  *  @file
      21             :  *    This file contains definitions for Device class. The objects of this
      22             :  *    class will be used by Controller applications to interact with CHIP
      23             :  *    devices. The class provides mechanism to construct, send and receive
      24             :  *    messages to and from the corresponding CHIP devices.
      25             :  */
      26             : 
      27             : #pragma once
      28             : 
      29             : #include <app/CASEClient.h>
      30             : #include <app/CASEClientPool.h>
      31             : #include <app/DeviceProxy.h>
      32             : #include <app/util/basic-types.h>
      33             : #include <credentials/GroupDataProvider.h>
      34             : #include <lib/address_resolve/AddressResolve.h>
      35             : #include <messaging/ExchangeContext.h>
      36             : #include <messaging/ExchangeDelegate.h>
      37             : #include <messaging/ExchangeMgr.h>
      38             : #include <messaging/Flags.h>
      39             : #include <messaging/ReliableMessageProtocolConfig.h>
      40             : #include <platform/CHIPDeviceConfig.h>
      41             : #include <protocols/secure_channel/CASESession.h>
      42             : #include <system/SystemClock.h>
      43             : #include <system/SystemLayer.h>
      44             : #include <transport/SessionManager.h>
      45             : #include <transport/TransportMgr.h>
      46             : #include <transport/raw/MessageHeader.h>
      47             : #include <transport/raw/UDP.h>
      48             : 
      49             : namespace chip {
      50             : 
      51             : class OperationalSessionSetup;
      52             : 
      53             : /**
      54             :  * @brief Delegate provided when creating OperationalSessionSetup.
      55             :  *
      56             :  * Once OperationalSessionSetup establishes a connection (or errors out) and has notified all
      57             :  * registered application callbacks via OnDeviceConnected/OnDeviceConnectionFailure, this delegate
      58             :  * is used to deallocate the OperationalSessionSetup.
      59             :  */
      60             : class OperationalSessionReleaseDelegate
      61             : {
      62             : public:
      63           1 :     virtual ~OperationalSessionReleaseDelegate()                        = default;
      64             :     virtual void ReleaseSession(OperationalSessionSetup * sessionSetup) = 0;
      65             : };
      66             : 
      67             : /**
      68             :  * @brief Minimal implementation of DeviceProxy that encapsulates a SessionHolder to track a CASE session.
      69             :  *
      70             :  * Deprecated - Avoid using this object.
      71             :  *
      72             :  * OperationalDeviceProxy is a minimal implementation of DeviceProxy. It is meant to provide a transition
      73             :  * for existing consumers of OperationalDeviceProxy that were delivered a reference to that object in
      74             :  * their respective OnDeviceConnected callback, but were incorrectly holding onto that object past
      75             :  * the function call. OperationalDeviceProxy can be held on for as long as is desired, while still
      76             :  * minimizing the code changes needed to transition to a more final solution by virtue of
      77             :  * implementing DeviceProxy.
      78             :  */
      79             : class OperationalDeviceProxy : public DeviceProxy
      80             : {
      81             : public:
      82           0 :     OperationalDeviceProxy(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle) :
      83           0 :         mExchangeMgr(exchangeMgr), mSecureSession(sessionHandle), mPeerScopedNodeId(sessionHandle->GetPeer())
      84           0 :     {}
      85           0 :     OperationalDeviceProxy() {}
      86             : 
      87           0 :     void Disconnect() override
      88             :     {
      89           0 :         if (IsSecureConnected())
      90             :         {
      91           0 :             GetSecureSession().Value()->AsSecureSession()->MarkAsDefunct();
      92             :         }
      93           0 :         mSecureSession.Release();
      94           0 :         mExchangeMgr      = nullptr;
      95           0 :         mPeerScopedNodeId = ScopedNodeId();
      96           0 :     }
      97           0 :     Messaging::ExchangeManager * GetExchangeManager() const override { return mExchangeMgr; }
      98           0 :     chip::Optional<SessionHandle> GetSecureSession() const override { return mSecureSession.Get(); }
      99           0 :     NodeId GetDeviceId() const override { return mPeerScopedNodeId.GetNodeId(); }
     100             :     ScopedNodeId GetPeerScopedNodeId() const { return mPeerScopedNodeId; }
     101             : 
     102             :     bool ConnectionReady() const { return (mExchangeMgr != nullptr && IsSecureConnected()); }
     103             : 
     104             : private:
     105           0 :     bool IsSecureConnected() const override { return static_cast<bool>(mSecureSession); }
     106             : 
     107             :     Messaging::ExchangeManager * mExchangeMgr = nullptr;
     108             :     SessionHolder mSecureSession;
     109             :     ScopedNodeId mPeerScopedNodeId;
     110             : };
     111             : 
     112             : /**
     113             :  * @brief Callback prototype when secure session is established.
     114             :  *
     115             :  * Callback implementations are not supposed to store the exchangeMgr or the sessionHandle. Older
     116             :  * application code does incorrectly hold onto this information so do not follow those incorrect
     117             :  * implementations as an example.
     118             :  */
     119             : typedef void (*OnDeviceConnected)(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle);
     120             : 
     121             : /**
     122             :  * Callback prototype when secure session establishment fails.
     123             :  */
     124             : typedef void (*OnDeviceConnectionFailure)(void * context, const ScopedNodeId & peerId, CHIP_ERROR error);
     125             : 
     126             : /**
     127             :  * Callback prototype when secure session establishement has failed and will be
     128             :  * retried.  retryTimeout indicates how much time will pass before we know
     129             :  * whether the retry has timed out waiting for a response to our Sigma1 message.
     130             :  */
     131             : typedef void (*OnDeviceConnectionRetry)(void * context, const ScopedNodeId & peerId, CHIP_ERROR error,
     132             :                                         System::Clock::Seconds16 retryTimeout);
     133             : 
     134             : /**
     135             :  * Object used to either establish a connection to peer or performing address lookup to a peer.
     136             :  *
     137             :  * OperationalSessionSetup is capable of either:
     138             :  *    1. Establishing a CASE session connection to a peer. Upon success or failure, the OnDeviceConnected or
     139             :  *       OnDeviceConnectionFailure callback will be called to notify the caller the results of trying to
     140             :  *       estblish a CASE session. Some additional details about the steps to establish a connection are:
     141             :  *          - Discover the device using DNSSD (find out what IP address to use and what
     142             :  *            communication parameters are appropriate for it)
     143             :  *          - Establish a secure channel to it via CASE
     144             :  *          - Expose to consumers the secure session for talking to the device via OnDeviceConnected
     145             :  *            callback.
     146             :  *    2. Performing an address lookup for given a scoped nodeid. On success, it will call into
     147             :  *       SessionManager to update the addresses for all matching sessions in the session table.
     148             :  *
     149             :  * OperationalSessionSetup has a very limited lifetime. Once it has completed its purpose outlined above,
     150             :  * it will use `releaseDelegate` to release itself.
     151             :  *
     152             :  * It is possible to determine which of the two purposes the OperationalSessionSetup is for by calling
     153             :  * IsForAddressUpdate().
     154             :  */
     155             : class DLL_EXPORT OperationalSessionSetup : public SessionEstablishmentDelegate, public AddressResolve::NodeListener
     156             : {
     157             : public:
     158             :     struct ConnnectionFailureInfo
     159             :     {
     160             :         const ScopedNodeId peerId;
     161             :         CHIP_ERROR error;
     162             :         SessionEstablishmentStage sessionStage;
     163             : 
     164           0 :         ConnnectionFailureInfo(const ScopedNodeId & peer, CHIP_ERROR err, SessionEstablishmentStage stage) :
     165           0 :             peerId(peer), error(err), sessionStage(stage)
     166           0 :         {}
     167             :     };
     168             : 
     169             :     using OnSetupFailure = void (*)(void * context, const ConnnectionFailureInfo & failureInfo);
     170             : 
     171             :     ~OperationalSessionSetup() override;
     172             : 
     173           0 :     OperationalSessionSetup(const CASEClientInitParams & params, CASEClientPoolDelegate * clientPool, ScopedNodeId peerId,
     174             :                             OperationalSessionReleaseDelegate * releaseDelegate)
     175           0 :     {
     176           0 :         mInitParams = params;
     177           0 :         if (params.Validate() != CHIP_NO_ERROR || clientPool == nullptr || releaseDelegate == nullptr)
     178             :         {
     179           0 :             mState = State::Uninitialized;
     180           0 :             return;
     181             :         }
     182             : 
     183           0 :         mClientPool      = clientPool;
     184           0 :         mPeerId          = peerId;
     185           0 :         mReleaseDelegate = releaseDelegate;
     186           0 :         mState           = State::NeedsAddress;
     187           0 :         mAddressLookupHandle.SetListener(this);
     188             :     }
     189             : 
     190             :     /*
     191             :      * This function can be called to establish a secure session with the device.
     192             :      *
     193             :      * The device is expected to have been commissioned, A CASE session
     194             :      * setup will be triggered.
     195             :      *
     196             :      * If session setup succeeds, the callback function `onConnection` will be called.
     197             :      * If session setup fails, `onFailure` will be called.
     198             :      *
     199             :      * If the session already exists, `onConnection` will be called immediately,
     200             :      * before the Connect call returns.
     201             :      *
     202             :      * `onFailure` may be called before the Connect call returns, for error
     203             :      * cases that are detected synchronously (e.g. inability to start an address
     204             :      * lookup).
     205             :      */
     206             :     void Connect(Callback::Callback<OnDeviceConnected> * onConnection, Callback::Callback<OnDeviceConnectionFailure> * onFailure);
     207             : 
     208             :     /*
     209             :      * This function can be called to establish a secure session with the device.
     210             :      *
     211             :      * The device is expected to have been commissioned, A CASE session
     212             :      * setup will be triggered.
     213             :      *
     214             :      * If session setup succeeds, the callback function `onConnection` will be called.
     215             :      * If session setup fails, `onSetupFailure` will be called.
     216             :      *
     217             :      * If the session already exists, `onConnection` will be called immediately,
     218             :      * before the Connect call returns.
     219             :      *
     220             :      * `onSetupFailure` may be called before the Connect call returns, for error cases that are detected synchronously
     221             :      * (e.g. inability to start an address lookup).
     222             :      */
     223             :     void Connect(Callback::Callback<OnDeviceConnected> * onConnection, Callback::Callback<OnSetupFailure> * onSetupFailure);
     224             : 
     225           0 :     bool IsForAddressUpdate() const { return mPerformingAddressUpdate; }
     226             : 
     227             :     //////////// SessionEstablishmentDelegate Implementation ///////////////
     228             :     void OnSessionEstablished(const SessionHandle & session) override;
     229             :     void OnSessionEstablishmentError(CHIP_ERROR error, SessionEstablishmentStage stage) override;
     230             : 
     231           0 :     ScopedNodeId GetPeerId() const { return mPeerId; }
     232             : 
     233             :     static Transport::PeerAddress ToPeerAddress(const Dnssd::ResolvedNodeData & nodeData)
     234             :     {
     235             :         Inet::InterfaceId interfaceId = Inet::InterfaceId::Null();
     236             : 
     237             :         // TODO - Revisit usage of InterfaceID only for addresses that are IPv6 LLA
     238             :         // Only use the DNS-SD resolution's InterfaceID for addresses that are IPv6 LLA.
     239             :         // For all other addresses, we should rely on the device's routing table to route messages sent.
     240             :         // Forcing messages down an InterfaceId might fail. For example, in bridged networks like Thread,
     241             :         // mDNS advertisements are not usually received on the same interface the peer is reachable on.
     242             :         if (nodeData.resolutionData.ipAddress[0].IsIPv6LinkLocal())
     243             :         {
     244             :             interfaceId = nodeData.resolutionData.interfaceId;
     245             :         }
     246             : 
     247             :         return Transport::PeerAddress::UDP(nodeData.resolutionData.ipAddress[0], nodeData.resolutionData.port, interfaceId);
     248             :     }
     249             : 
     250             :     /**
     251             :      * @brief Get the fabricIndex
     252             :      */
     253           0 :     FabricIndex GetFabricIndex() const { return mPeerId.GetFabricIndex(); }
     254             : 
     255             :     void PerformAddressUpdate();
     256             : 
     257             :     // AddressResolve::NodeListener - notifications when dnssd finds a node IP address
     258             :     void OnNodeAddressResolved(const PeerId & peerId, const AddressResolve::ResolveResult & result) override;
     259             :     void OnNodeAddressResolutionFailed(const PeerId & peerId, CHIP_ERROR reason) override;
     260             : 
     261             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     262             :     // Update our remaining attempt count to be at least the given value.
     263             :     void UpdateAttemptCount(uint8_t attemptCount);
     264             : 
     265             :     // Add a retry handler for this session setup.
     266             :     void AddRetryHandler(Callback::Callback<OnDeviceConnectionRetry> * onRetry);
     267             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     268             : 
     269             : private:
     270             :     enum class State : uint8_t
     271             :     {
     272             :         Uninitialized,    // Error state: OperationalSessionSetup is useless
     273             :         NeedsAddress,     // No address known, lookup not started yet.
     274             :         ResolvingAddress, // Address lookup in progress.
     275             :         HasAddress,       // Have an address, CASE handshake not started yet.
     276             :         Connecting,       // CASE handshake in progress.
     277             :         SecureConnected,  // CASE session established.
     278             :         WaitingForRetry,  // No address known, but a retry is pending.  Added at
     279             :                           // end to make logs easier to understand.
     280             :     };
     281             : 
     282             :     CASEClientInitParams mInitParams;
     283             :     CASEClientPoolDelegate * mClientPool = nullptr;
     284             : 
     285             :     // mCASEClient is only non-null if we are in State::Connecting or just
     286             :     // allocated it as part of an attempt to enter State::Connecting.
     287             :     CASEClient * mCASEClient = nullptr;
     288             : 
     289             :     ScopedNodeId mPeerId;
     290             : 
     291             :     Transport::PeerAddress mDeviceAddress = Transport::PeerAddress::UDP(Inet::IPAddress::Any);
     292             : 
     293             :     SessionHolder mSecureSession;
     294             : 
     295             :     Callback::CallbackDeque mConnectionSuccess;
     296             :     Callback::CallbackDeque mConnectionFailure;
     297             :     Callback::CallbackDeque mSetupFailure;
     298             : 
     299             :     OperationalSessionReleaseDelegate * mReleaseDelegate;
     300             : 
     301             :     /// This is used when a node address is required.
     302             :     chip::AddressResolve::NodeLookupHandle mAddressLookupHandle;
     303             : 
     304             :     State mState = State::Uninitialized;
     305             : 
     306             :     bool mPerformingAddressUpdate = false;
     307             : 
     308             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     309             :     // When we TryNextResult on the resolver, it will synchronously call back
     310             :     // into our OnNodeAddressResolved when it succeeds.  We need to track
     311             :     // whether the OnNodeAddressResolved is coming from handling a session
     312             :     // establishment error or whether it's happening because we didn't even
     313             :     // manage to start a session establishment at all.  Use this member to keep
     314             :     // track of that.
     315             :     bool mTryingNextResultDueToSessionEstablishmentError = false;
     316             : 
     317             :     uint8_t mRemainingAttempts = 0;
     318             :     uint8_t mAttemptsDone      = 0;
     319             : 
     320             :     uint8_t mResolveAttemptsAllowed = 0;
     321             : 
     322             :     Callback::CallbackDeque mConnectionRetry;
     323             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     324             : 
     325             :     void MoveToState(State aTargetState);
     326             : 
     327             :     CHIP_ERROR EstablishConnection(const ReliableMessageProtocolConfig & config);
     328             : 
     329             :     /*
     330             :      * This checks to see if an existing CASE session exists to the peer within the SessionManager
     331             :      * and if one exists, to load that into mSecureSession.
     332             :      *
     333             :      * Returns true if a valid session was found, false otherwise.
     334             :      *
     335             :      */
     336             :     bool AttachToExistingSecureSession();
     337             : 
     338             :     void CleanupCASEClient();
     339             : 
     340             :     void Connect(Callback::Callback<OnDeviceConnected> * onConnection, Callback::Callback<OnDeviceConnectionFailure> * onFailure,
     341             :                  Callback::Callback<OnSetupFailure> * onSetupFailure);
     342             : 
     343             :     void EnqueueConnectionCallbacks(Callback::Callback<OnDeviceConnected> * onConnection,
     344             :                                     Callback::Callback<OnDeviceConnectionFailure> * onFailure,
     345             :                                     Callback::Callback<OnSetupFailure> * onSetupFailure);
     346             : 
     347             :     enum class ReleaseBehavior
     348             :     {
     349             :         Release,
     350             :         DoNotRelease
     351             :     };
     352             : 
     353             :     /*
     354             :      * This dequeues all failure and success callbacks and appropriately invokes either set depending
     355             :      * on the value of error.
     356             :      *
     357             :      * If error == CHIP_NO_ERROR, only success callbacks are invoked. Otherwise, only failure callbacks are invoked.
     358             :      *
     359             :      * The state offers additional context regarding the failure, indicating the specific state in which
     360             :      * the error occurs. It is only relayed through failure callbacks when the error is not equal to CHIP_NO_ERROR.
     361             :      *
     362             :      * If releaseBehavior is Release, this uses mReleaseDelegate to release
     363             :      * ourselves (aka `this`). As a result any caller should return right away
     364             :      * without touching `this`.
     365             :      *
     366             :      * Setting releaseBehavior to DoNotRelease is meant for use from the destructor
     367             :      */
     368             :     void DequeueConnectionCallbacks(CHIP_ERROR error, SessionEstablishmentStage stage,
     369             :                                     ReleaseBehavior releaseBehavior = ReleaseBehavior::Release);
     370             : 
     371           0 :     void DequeueConnectionCallbacks(CHIP_ERROR error, ReleaseBehavior releaseBehavior = ReleaseBehavior::Release)
     372             :     {
     373           0 :         this->DequeueConnectionCallbacks(error, SessionEstablishmentStage::kNotInKeyExchange, releaseBehavior);
     374           0 :     }
     375             : 
     376             :     /**
     377             :      * Helper for DequeueConnectionCallbacks that handles the actual callback
     378             :      * notifications. This happens after the object has been released, if it's
     379             :      * being released.
     380             :      */
     381             :     static void NotifyConnectionCallbacks(Callback::Cancelable & failureReady, Callback::Cancelable & setupFailureReady,
     382             :                                           Callback::Cancelable & successReady, CHIP_ERROR error, SessionEstablishmentStage stage,
     383             :                                           const ScopedNodeId & peerId, bool performingAddressUpdate,
     384             :                                           Messaging::ExchangeManager * exchangeMgr,
     385             :                                           const Optional<SessionHandle> & optionalSessionHandle);
     386             : 
     387             :     /**
     388             :      * Triggers a DNSSD lookup to find a usable peer address.
     389             :      */
     390             :     CHIP_ERROR LookupPeerAddress();
     391             : 
     392             :     /**
     393             :      * This function will set new IP address, port and MRP retransmission intervals of the device.
     394             :      */
     395             :     void UpdateDeviceData(const Transport::PeerAddress & addr, const ReliableMessageProtocolConfig & config);
     396             : 
     397             : #if CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     398             :     /**
     399             :      * Schedule a setup reattempt, if possible.  The outparam indicates how long
     400             :      * it will be before the reattempt happens.
     401             :      */
     402             :     CHIP_ERROR ScheduleSessionSetupReattempt(System::Clock::Seconds16 & timerDelay);
     403             : 
     404             :     /**
     405             :      * Cancel a scheduled setup reattempt, if we can (i.e. if we still have
     406             :      * access to the SystemLayer).
     407             :      */
     408             :     void CancelSessionSetupReattempt();
     409             : 
     410             :     /**
     411             :      * Helper for our backoff retry timer.
     412             :      */
     413             :     static void TrySetupAgain(System::Layer * systemLayer, void * state);
     414             : 
     415             :     /**
     416             :      * Helper to notify our retry callbacks that a setup error occurred and we
     417             :      * will retry.
     418             :      */
     419             :     void NotifyRetryHandlers(CHIP_ERROR error, const ReliableMessageProtocolConfig & remoteMrpConfig,
     420             :                              System::Clock::Seconds16 retryDelay);
     421             : 
     422             :     /**
     423             :      * A version of NotifyRetryHandlers that passes in a retry timeout estimate
     424             :      * directly.
     425             :      */
     426             :     void NotifyRetryHandlers(CHIP_ERROR error, System::Clock::Seconds16 timeoutEstimate);
     427             : #endif // CHIP_DEVICE_CONFIG_ENABLE_AUTOMATIC_CASE_RETRIES
     428             : };
     429             : 
     430             : } // namespace chip

Generated by: LCOV version 1.14