/* * * Copyright (c) 2020 Project CHIP Authors * * 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION #include #endif #include #include #include #include #include #if CONFIG_NETWORK_LAYER_BLE #include #endif #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF #include #endif #include #include #include #if CHIP_CONFIG_ENABLE_ICD_SERVER #include // nogncheck #if CHIP_CONFIG_ENABLE_ICD_CIP #include // nogncheck #include // nogncheck #endif // CHIP_CONFIG_ENABLE_ICD_CIP #endif // CHIP_CONFIG_ENABLE_ICD_SERVER namespace chip { inline constexpr size_t kMaxBlePendingPackets = 1; #if INET_CONFIG_ENABLE_TCP_ENDPOINT inline constexpr size_t kMaxTcpActiveConnectionCount = CHIP_CONFIG_MAX_ACTIVE_TCP_CONNECTIONS; inline constexpr size_t kMaxTcpPendingPackets = CHIP_CONFIG_MAX_TCP_PENDING_PACKETS; #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT // // NOTE: Please do not alter the order of template specialization here as the logic // in the Server impl depends on this. // using ServerTransportMgr = chip::TransportMgr #endif #if INET_CONFIG_ENABLE_TCP_ENDPOINT , chip::Transport::TCP #endif #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF , chip::Transport::WiFiPAFBase #endif >; #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT using UdcTransportMgr = TransportMgr; #endif struct ServerInitParams { ServerInitParams() = default; // Not copyable ServerInitParams(const ServerInitParams &) = delete; ServerInitParams & operator=(const ServerInitParams &) = delete; // Application delegate to handle some commissioning lifecycle events AppDelegate * appDelegate = nullptr; // Port to use for Matter commissioning/operational traffic uint16_t operationalServicePort = CHIP_PORT; // Port to use for UDC if supported uint16_t userDirectedCommissioningPort = CHIP_UDC_PORT; // Interface on which to run daemon Inet::InterfaceId interfaceId = Inet::InterfaceId::Null(); // Persistent storage delegate: MUST be injected. Used to maintain storage by much common code. // Must be initialized before being provided. PersistentStorageDelegate * persistentStorageDelegate = nullptr; // Session resumption storage: Optional. Support session resumption when provided. // Must be initialized before being provided. SessionResumptionStorage * sessionResumptionStorage = nullptr; // Session resumption storage: Optional. Support session resumption when provided. // Must be initialized before being provided. app::SubscriptionResumptionStorage * subscriptionResumptionStorage = nullptr; // Certificate validity policy: Optional. If none is injected, CHIPCert // enforces a default policy. Credentials::CertificateValidityPolicy * certificateValidityPolicy = nullptr; // Group data provider: MUST be injected. Used to maintain critical keys such as the Identity // Protection Key (IPK) for CASE. Must be initialized before being provided. Credentials::GroupDataProvider * groupDataProvider = nullptr; // Session keystore: MUST be injected. Used to derive and manage lifecycle of symmetric keys. Crypto::SessionKeystore * sessionKeystore = nullptr; // Access control delegate: MUST be injected. Used to look up access control rules. Must be // initialized before being provided. Access::AccessControl::Delegate * accessDelegate = nullptr; // ACL storage: MUST be injected. Used to store ACL entries in persistent storage. Must NOT // be initialized before being provided. app::AclStorage * aclStorage = nullptr; #if CHIP_CONFIG_USE_ACCESS_RESTRICTIONS // Access Restriction implementation: MUST be injected if MNGD feature enabled. Used to enforce // access restrictions that are managed by the device. Access::AccessRestrictionProvider * accessRestrictionProvider = nullptr; #endif // Network native params can be injected depending on the // selected Endpoint implementation void * endpointNativeParams = nullptr; // Optional. Support test event triggers when provided. Must be initialized before being // provided. TestEventTriggerDelegate * testEventTriggerDelegate = nullptr; // Operational keystore with access to the operational keys: MUST be injected. Crypto::OperationalKeystore * operationalKeystore = nullptr; // Operational certificate store with access to the operational certs in persisted storage: // must not be null at timne of Server::Init(). Credentials::OperationalCertificateStore * opCertStore = nullptr; // Required, if not provided, the Server::Init() WILL fail. app::reporting::ReportScheduler * reportScheduler = nullptr; #if CHIP_CONFIG_ENABLE_ICD_CIP // Optional. Support for the ICD Check-In BackOff strategy. Must be initialized before being provided. // If the ICD Check-In protocol use-case is supported and no strategy is provided, server will use the default strategy. app::ICDCheckInBackOffStrategy * icdCheckInBackOffStrategy = nullptr; #endif // CHIP_CONFIG_ENABLE_ICD_CIP }; /** * Transitional version of ServerInitParams to assist SDK integrators in * transitioning to injecting product/platform-owned resources. This version * of `ServerInitParams` statically owns and initializes (via the * `InitializeStaticResourcesBeforeServerInit()` method) the persistent storage * delegate, the group data provider, and the access control delegate. This is to reduce * the amount of copied boilerplate in all the example initializations (e.g. AppTask.cpp, * main.cpp). * * This version SHOULD BE USED ONLY FOR THE IN-TREE EXAMPLES. * * ACTION ITEMS FOR TRANSITION from a example in-tree to a product: * * While this could be used indefinitely, it does not exemplify orderly management of * application-injected resources. It is recommended for actual products to instead: * - Use the basic ServerInitParams in the application * - Have the application own an instance of the resources being injected in its own * state (e.g. an implementation of PersistentStorageDelegate and GroupDataProvider * interfaces). * - Initialize the injected resources prior to calling Server::Init() * - De-initialize the injected resources after calling Server::Shutdown() * * WARNING: DO NOT replicate the pattern shown here of having a subclass of ServerInitParams * own the resources outside of examples. This was done to reduce the amount of change * to existing examples while still supporting non-example versions of the * resources to be injected. */ struct CommonCaseDeviceServerInitParams : public ServerInitParams { CommonCaseDeviceServerInitParams() = default; // Not copyable CommonCaseDeviceServerInitParams(const CommonCaseDeviceServerInitParams &) = delete; CommonCaseDeviceServerInitParams & operator=(const CommonCaseDeviceServerInitParams &) = delete; /** * Call this before Server::Init() to initialize the internally-owned resources. * Server::Init() will fail if this is not done, since several params required to * be non-null will be null without calling this method. ** See the transition method * in the outer comment of this class **. * * @return CHIP_NO_ERROR on success or a CHIP_ERROR value from APIs called to initialize * resources on failure. */ CHIP_ERROR InitializeStaticResourcesBeforeServerInit() { // KVS-based persistent storage delegate injection if (persistentStorageDelegate == nullptr) { chip::DeviceLayer::PersistedStorage::KeyValueStoreManager & kvsManager = DeviceLayer::PersistedStorage::KeyValueStoreMgr(); ReturnErrorOnFailure(sKvsPersistenStorageDelegate.Init(&kvsManager)); this->persistentStorageDelegate = &sKvsPersistenStorageDelegate; } // PersistentStorageDelegate "software-based" operational key access injection if (this->operationalKeystore == nullptr) { // WARNING: PersistentStorageOperationalKeystore::Finish() is never called. It's fine for // for examples and for now. ReturnErrorOnFailure(sPersistentStorageOperationalKeystore.Init(this->persistentStorageDelegate)); this->operationalKeystore = &sPersistentStorageOperationalKeystore; } // OpCertStore can be injected but default to persistent storage default // for simplicity of the examples. if (this->opCertStore == nullptr) { // WARNING: PersistentStorageOpCertStore::Finish() is never called. It's fine for // for examples and for now, since all storage is immediate for that impl. ReturnErrorOnFailure(sPersistentStorageOpCertStore.Init(this->persistentStorageDelegate)); this->opCertStore = &sPersistentStorageOpCertStore; } // Injection of report scheduler WILL lead to two schedulers being allocated. As recommended above, this should only be used // for IN-TREE examples. If a default scheduler is desired, the basic ServerInitParams should be used by the application and // CommonCaseDeviceServerInitParams should not be allocated. if (this->reportScheduler == nullptr) { reportScheduler = &sReportScheduler; } // Session Keystore injection this->sessionKeystore = &sSessionKeystore; // Group Data provider injection sGroupDataProvider.SetStorageDelegate(this->persistentStorageDelegate); sGroupDataProvider.SetSessionKeystore(this->sessionKeystore); ReturnErrorOnFailure(sGroupDataProvider.Init()); this->groupDataProvider = &sGroupDataProvider; #if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION ReturnErrorOnFailure(sSessionResumptionStorage.Init(this->persistentStorageDelegate)); this->sessionResumptionStorage = &sSessionResumptionStorage; #else this->sessionResumptionStorage = nullptr; #endif // Inject access control delegate this->accessDelegate = Access::Examples::GetAccessControlDelegate(); // Inject ACL storage. (Don't initialize it.) this->aclStorage = &sAclStorage; #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS ChipLogProgress(AppServer, "Initializing subscription resumption storage..."); ReturnErrorOnFailure(sSubscriptionResumptionStorage.Init(this->persistentStorageDelegate)); this->subscriptionResumptionStorage = &sSubscriptionResumptionStorage; #else ChipLogProgress(AppServer, "Subscription persistence not supported"); #endif #if CHIP_CONFIG_ENABLE_ICD_CIP if (this->icdCheckInBackOffStrategy == nullptr) { this->icdCheckInBackOffStrategy = &sDefaultICDCheckInBackOffStrategy; } #endif return CHIP_NO_ERROR; } private: static KvsPersistentStorageDelegate sKvsPersistenStorageDelegate; static PersistentStorageOperationalKeystore sPersistentStorageOperationalKeystore; static Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore; static Credentials::GroupDataProviderImpl sGroupDataProvider; static chip::app::DefaultTimerDelegate sTimerDelegate; static app::reporting::ReportSchedulerImpl sReportScheduler; #if CHIP_CONFIG_ENABLE_SESSION_RESUMPTION static SimpleSessionResumptionStorage sSessionResumptionStorage; #endif #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS static app::SimpleSubscriptionResumptionStorage sSubscriptionResumptionStorage; #endif static app::DefaultAclStorage sAclStorage; static Crypto::DefaultSessionKeystore sSessionKeystore; #if CHIP_CONFIG_ENABLE_ICD_CIP static app::DefaultICDCheckInBackOffStrategy sDefaultICDCheckInBackOffStrategy; #endif }; /** * The `Server` singleton class is an aggregate for all the resources needed to run a * Node that is both Commissionable and mainly used as an end-node with server clusters. * In other words, it aggregates the state needed for the type of Node used for most * products that are not mainly controller/administrator role. * * This sington class expects `ServerInitParams` initialization parameters but does not * own the resources injected from `ServerInitParams`. Any object pointers/references * passed in ServerInitParams must be pre-initialized externally, and shutdown/finalized * after `Server::Shutdown()` is called. * * TODO: Separate lifecycle ownership for some more capabilities that should not belong to * common logic, such as `GenerateShutDownEvent`. * * TODO: Replace all uses of GetInstance() to "reach in" to this state from all cluster * server common logic that deal with global node state with either a common NodeState * compatible with OperationalDeviceProxy/DeviceProxy, or with injection at common * SDK logic init. */ class Server { public: CHIP_ERROR Init(const ServerInitParams & initParams); #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT CHIP_ERROR SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress commissioner, Protocols::UserDirectedCommissioning::IdentificationDeclaration & id); Protocols::UserDirectedCommissioning::UserDirectedCommissioningClient * GetUserDirectedCommissioningClient() { return gUDCClient; } #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT /** * @brief Call this function to rejoin existing groups found in the GroupDataProvider */ void RejoinExistingMulticastGroups(); FabricTable & GetFabricTable() { return mFabrics; } CASESessionManager * GetCASESessionManager() { return &mCASESessionManager; } Messaging::ExchangeManager & GetExchangeManager() { return mExchangeMgr; } SessionManager & GetSecureSessionManager() { return mSessions; } SessionResumptionStorage * GetSessionResumptionStorage() { return mSessionResumptionStorage; } app::SubscriptionResumptionStorage * GetSubscriptionResumptionStorage() { return mSubscriptionResumptionStorage; } TransportMgrBase & GetTransportManager() { return mTransports; } Credentials::GroupDataProvider * GetGroupDataProvider() { return mGroupsProvider; } Crypto::SessionKeystore * GetSessionKeystore() const { return mSessionKeystore; } #if CONFIG_NETWORK_LAYER_BLE Ble::BleLayer * GetBleLayerObject() { return mBleLayer; } #endif CommissioningWindowManager & GetCommissioningWindowManager() { return mCommissioningWindowManager; } PersistentStorageDelegate & GetPersistentStorage() { return *mDeviceStorage; } app::FailSafeContext & GetFailSafeContext() { return mFailSafeContext; } TestEventTriggerDelegate * GetTestEventTriggerDelegate() { return mTestEventTriggerDelegate; } Crypto::OperationalKeystore * GetOperationalKeystore() { return mOperationalKeystore; } Credentials::OperationalCertificateStore * GetOpCertStore() { return mOpCertStore; } app::DefaultAttributePersistenceProvider & GetDefaultAttributePersister() { return mAttributePersister; } app::reporting::ReportScheduler * GetReportScheduler() { return mReportScheduler; } #if CHIP_CONFIG_ENABLE_ICD_SERVER app::ICDManager & GetICDManager() { return mICDManager; } #if CHIP_CONFIG_ENABLE_ICD_CIP /** * @brief Function to determine if a Check-In message would be sent at Boot up * * @param aFabricIndex client fabric index * @param subjectID client subject ID * @return true Check-In message would be sent on boot up. * @return false Device has a persisted subscription with the client. See CHIP_CONFIG_PERSIST_SUBSCRIPTIONS. */ bool ShouldCheckInMsgsBeSentAtBootFunction(FabricIndex aFabricIndex, NodeId subjectID); #endif // CHIP_CONFIG_ENABLE_ICD_CIP #endif // CHIP_CONFIG_ENABLE_ICD_SERVER /** * This function causes the ShutDown event to be generated async on the * Matter event loop. Should be called before stopping the event loop. */ void GenerateShutDownEvent(); void Shutdown(); void ScheduleFactoryReset(); System::Clock::Microseconds64 TimeSinceInit() const { return System::SystemClock().GetMonotonicMicroseconds64() - mInitTimestamp; } static Server & GetInstance() { return sServer; } private: Server() {} static Server sServer; void InitFailSafe(); void OnPlatformEvent(const DeviceLayer::ChipDeviceEvent & event); void CheckServerReadyEvent(); static void OnPlatformEventWrapper(const DeviceLayer::ChipDeviceEvent * event, intptr_t); #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS /** * @brief Called at Server::Init time to resume persisted subscriptions if the feature flag is enabled */ void ResumeSubscriptions(); #endif class GroupDataProviderListener final : public Credentials::GroupDataProvider::GroupListener { public: GroupDataProviderListener() {} CHIP_ERROR Init(Server * server) { VerifyOrReturnError(server != nullptr, CHIP_ERROR_INVALID_ARGUMENT); mServer = server; return CHIP_NO_ERROR; }; void OnGroupAdded(chip::FabricIndex fabric_index, const Credentials::GroupDataProvider::GroupInfo & new_group) override { const FabricInfo * fabric = mServer->GetFabricTable().FindFabricWithIndex(fabric_index); if (fabric == nullptr) { ChipLogError(AppServer, "Group added to nonexistent fabric?"); return; } if (mServer->GetTransportManager().MulticastGroupJoinLeave( Transport::PeerAddress::Multicast(fabric->GetFabricId(), new_group.group_id), true) != CHIP_NO_ERROR) { ChipLogError(AppServer, "Unable to listen to group"); } }; void OnGroupRemoved(chip::FabricIndex fabric_index, const Credentials::GroupDataProvider::GroupInfo & old_group) override { const FabricInfo * fabric = mServer->GetFabricTable().FindFabricWithIndex(fabric_index); if (fabric == nullptr) { ChipLogError(AppServer, "Group removed from nonexistent fabric?"); return; } mServer->GetTransportManager().MulticastGroupJoinLeave( Transport::PeerAddress::Multicast(fabric->GetFabricId(), old_group.group_id), false); }; private: Server * mServer; }; class ServerFabricDelegate final : public chip::FabricTable::Delegate { public: ServerFabricDelegate() {} CHIP_ERROR Init(Server * server) { VerifyOrReturnError(server != nullptr, CHIP_ERROR_INVALID_ARGUMENT); mServer = server; return CHIP_NO_ERROR; } void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override { (void) fabricTable; ClearCASEResumptionStateOnFabricChange(fabricIndex); ClearSubscriptionResumptionStateOnFabricChange(fabricIndex); Credentials::GroupDataProvider * groupDataProvider = mServer->GetGroupDataProvider(); if (groupDataProvider != nullptr) { CHIP_ERROR err = groupDataProvider->RemoveFabric(fabricIndex); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Warning, failed to delete GroupDataProvider state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), err.Format()); } } // Remove access control entries in reverse order (it could be any order, but reverse order // will cause less churn in persistent storage). CHIP_ERROR aclErr = Access::GetAccessControl().DeleteAllEntriesForFabric(fabricIndex); if (aclErr != CHIP_NO_ERROR) { ChipLogError(AppServer, "Warning, failed to delete access control state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), aclErr.Format()); } // Remove ACL extension entry for the given fabricIndex. auto & storage = mServer->GetPersistentStorage(); aclErr = storage.SyncDeleteKeyValue(DefaultStorageKeyAllocator::AccessControlExtensionEntry(fabricIndex).KeyName()); if (aclErr != CHIP_NO_ERROR && aclErr != CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) { ChipLogError(AppServer, "Warning, failed to delete ACL extension entry for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), aclErr.Format()); } mServer->GetCommissioningWindowManager().OnFabricRemoved(fabricIndex); } void OnFabricUpdated(const FabricTable & fabricTable, chip::FabricIndex fabricIndex) override { (void) fabricTable; ClearCASEResumptionStateOnFabricChange(fabricIndex); } private: void ClearCASEResumptionStateOnFabricChange(chip::FabricIndex fabricIndex) { auto * sessionResumptionStorage = mServer->GetSessionResumptionStorage(); VerifyOrReturn(sessionResumptionStorage != nullptr); CHIP_ERROR err = sessionResumptionStorage->DeleteAll(fabricIndex); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Warning, failed to delete session resumption state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), err.Format()); } } void ClearSubscriptionResumptionStateOnFabricChange(chip::FabricIndex fabricIndex) { auto * subscriptionResumptionStorage = mServer->GetSubscriptionResumptionStorage(); VerifyOrReturn(subscriptionResumptionStorage != nullptr); CHIP_ERROR err = subscriptionResumptionStorage->DeleteAll(fabricIndex); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Warning, failed to delete subscription resumption state for fabric index 0x%x: %" CHIP_ERROR_FORMAT, static_cast(fabricIndex), err.Format()); } } Server * mServer = nullptr; }; /** * Since root certificates for Matter nodes cannot be updated in a reasonable * way, it doesn't make sense to enforce expiration time on root certificates. * This policy allows through root certificates, even if they're expired, and * otherwise delegates to the provided policy, or to the default policy if no * policy is provided. */ class IgnoreRootExpirationValidityPolicy : public Credentials::CertificateValidityPolicy { public: IgnoreRootExpirationValidityPolicy() {} void Init(Credentials::CertificateValidityPolicy * providedPolicy) { mProvidedPolicy = providedPolicy; } CHIP_ERROR ApplyCertificateValidityPolicy(const Credentials::ChipCertificateData * cert, uint8_t depth, Credentials::CertificateValidityResult result) override { switch (result) { case Credentials::CertificateValidityResult::kExpired: case Credentials::CertificateValidityResult::kExpiredAtLastKnownGoodTime: case Credentials::CertificateValidityResult::kTimeUnknown: { Credentials::CertType certType; ReturnErrorOnFailure(cert->mSubjectDN.GetCertType(certType)); if (certType == Credentials::CertType::kRoot) { return CHIP_NO_ERROR; } break; } default: break; } if (mProvidedPolicy) { return mProvidedPolicy->ApplyCertificateValidityPolicy(cert, depth, result); } return Credentials::CertificateValidityPolicy::ApplyDefaultPolicy(cert, depth, result); } private: Credentials::CertificateValidityPolicy * mProvidedPolicy = nullptr; }; #if CONFIG_NETWORK_LAYER_BLE Ble::BleLayer * mBleLayer = nullptr; #endif // By default, use a certificate validation policy compatible with non-wall-clock-time-synced // embedded systems. static Credentials::IgnoreCertificateValidityPeriodPolicy sDefaultCertValidityPolicy; ServerTransportMgr mTransports; SessionManager mSessions; CASEServer mCASEServer; CASESessionManager mCASESessionManager; CASEClientPool mCASEClientPool; OperationalSessionSetupPool mSessionSetupPool; Protocols::SecureChannel::UnsolicitedStatusHandler mUnsolicitedStatusHandler; Messaging::ExchangeManager mExchangeMgr; FabricTable mFabrics; secure_channel::MessageCounterManager mMessageCounterManager; #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT Protocols::UserDirectedCommissioning::UserDirectedCommissioningClient * gUDCClient = nullptr; // mUdcTransportMgr is for insecure communication (ex. user directed commissioning) // specifically, the commissioner declaration message (sent by commissioner to commissionee) UdcTransportMgr * mUdcTransportMgr = nullptr; uint16_t mCdcListenPort = CHIP_UDC_COMMISSIONEE_PORT; #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT CommissioningWindowManager mCommissioningWindowManager; IgnoreRootExpirationValidityPolicy mCertificateValidityPolicy; PersistentStorageDelegate * mDeviceStorage; SessionResumptionStorage * mSessionResumptionStorage; app::SubscriptionResumptionStorage * mSubscriptionResumptionStorage; Credentials::GroupDataProvider * mGroupsProvider; Crypto::SessionKeystore * mSessionKeystore; app::DefaultAttributePersistenceProvider mAttributePersister; GroupDataProviderListener mListener; ServerFabricDelegate mFabricDelegate; app::reporting::ReportScheduler * mReportScheduler; Access::AccessControl mAccessControl; app::AclStorage * mAclStorage; TestEventTriggerDelegate * mTestEventTriggerDelegate; Crypto::OperationalKeystore * mOperationalKeystore; Credentials::OperationalCertificateStore * mOpCertStore; app::FailSafeContext mFailSafeContext; bool mIsDnssdReady = false; uint16_t mOperationalServicePort; uint16_t mUserDirectedCommissioningPort; Inet::InterfaceId mInterfaceId; System::Clock::Microseconds64 mInitTimestamp; #if CHIP_CONFIG_ENABLE_ICD_SERVER app::ICDManager mICDManager; #endif // CHIP_CONFIG_ENABLE_ICD_SERVER }; } // namespace chip