diff --git a/examples/lit-icd-app/linux/args.gni b/examples/lit-icd-app/linux/args.gni index 50691e9c530522..5630bb4e1ccb25 100644 --- a/examples/lit-icd-app/linux/args.gni +++ b/examples/lit-icd-app/linux/args.gni @@ -22,7 +22,8 @@ chip_system_project_config_include = "" chip_project_config_include_dirs = [ "${chip_root}/examples/lit-icd-app/linux/include" ] -chip_project_config_include_dirs += [ "${chip_root}/config/standalone" ] +chip_project_config_include_dirs += + [ "${chip_root}/examples/lit-icd-app/linux/config/" ] matter_enable_tracing_support = true # ICD configurations diff --git a/examples/lit-icd-app/linux/config/CHIPProjectConfig.h b/examples/lit-icd-app/linux/config/CHIPProjectConfig.h new file mode 100644 index 00000000000000..61fbf50340833c --- /dev/null +++ b/examples/lit-icd-app/linux/config/CHIPProjectConfig.h @@ -0,0 +1,88 @@ +/* + * + * Copyright (c) 2020-2022 Project CHIP Authors + * Copyright (c) 2016-2017 Nest Labs, Inc. + * + * 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 + * CHIP project configuration for standalone builds on Linux and OS X. + * + */ +#ifndef CHIPPROJECTCONFIG_H +#define CHIPPROJECTCONFIG_H + +#define CHIP_CONFIG_EVENT_LOGGING_NUM_EXTERNAL_CALLBACKS 2 + +// Uncomment this for a large Tunnel MTU. +// #define CHIP_CONFIG_TUNNEL_INTERFACE_MTU (9000) + +// Enable support functions for parsing command-line arguments +#define CHIP_CONFIG_ENABLE_ARG_PARSER 1 + +// Enable use of test setup parameters for testing purposes only. +// +// WARNING: This option makes it possible to circumvent basic chip security functionality. +// Because of this it SHOULD NEVER BE ENABLED IN PRODUCTION BUILDS. +// +#ifndef CHIP_DEVICE_CONFIG_ENABLE_TEST_SETUP_PARAMS +#define CHIP_DEVICE_CONFIG_ENABLE_TEST_SETUP_PARAMS 1 +#endif + +// Enable reading DRBG seed data from /dev/(u)random. +// This is needed for test applications and the CHIP device manager to function +// properly when CHIP_CONFIG_RNG_IMPLEMENTATION_CHIPDRBG is enabled. +#define CHIP_CONFIG_DEV_RANDOM_DRBG_SEED 1 + +// For convenience, Chip Security Test Mode can be enabled and the +// requirement for authentication in various protocols can be disabled. +// +// WARNING: These options make it possible to circumvent basic Chip security functionality, +// including message encryption. Because of this they MUST NEVER BE ENABLED IN PRODUCTION BUILDS. +// +// To build with this flag, pass 'treat_warnings_as_errors=false' to gn/ninja. +// +#define CHIP_CONFIG_SECURITY_TEST_MODE 0 + +#define CHIP_CONFIG_ENABLE_UPDATE 1 + +#define CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE 0 + +#define CHIP_CONFIG_DATA_MANAGEMENT_CLIENT_EXPERIMENTAL 1 + +#ifndef CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT +#define CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT 4 +#endif + +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION 1 +#endif + +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING "1.0" +#endif + +// +// Default of 8 ECs is not sufficient for some of the unit tests +// that try to validate multiple simultaneous interactions. +// In tests like TestReadHandler_MultipleSubscriptions, we are trying to issue as many read / subscription requests as possible in +// parallel. Since the default config says we support 16 fabrics, and we will have 4 read handlers for each fabric (3 subscriptions +// + 1 reserved for read) that is read transactions in parallel. Since the report handlers are allocated on the heap, we will issue +// 65 requests (the TestReadHandler_MultipleSubscriptions will issue CHIP_IM_MAX_NUM_READ_HANDLER + 1 subscriptions to verify heap +// allocation logic) in total and that is 130 ECs. Round this up to 150 ECs +// +#define CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS 150 + +#endif /* CHIPPROJECTCONFIG_H */ diff --git a/examples/lit-icd-app/linux/config/SystemProjectConfig.h b/examples/lit-icd-app/linux/config/SystemProjectConfig.h new file mode 100644 index 00000000000000..13035c0248fcd5 --- /dev/null +++ b/examples/lit-icd-app/linux/config/SystemProjectConfig.h @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * + * 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 + * chip::System project configuration for standalone builds on Linux and OS X. + * + */ +#ifndef SYSTEMPROJECTCONFIG_H +#define SYSTEMPROJECTCONFIG_H + +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +// Uncomment this for larger buffers (e.g. to support a bigger CHIP_CONFIG_TUNNEL_INTERFACE_MTU). +// #define CHIP_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX 9050 +#endif + +#endif /* SYSTEMPROJECTCONFIG_H */ diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp index d202dc7d07c062..10c81efc16ee40 100644 --- a/src/app/InteractionModelEngine.cpp +++ b/src/app/InteractionModelEngine.cpp @@ -278,6 +278,37 @@ CHIP_ERROR InteractionModelEngine::ShutdownSubscription(const ScopedNodeId & aPe return CHIP_ERROR_KEY_NOT_FOUND; } +bool InteractionModelEngine::IsMatchingSubscriptionActive(const FabricIndex & aFabricIndex, const NodeId & subjectID) +{ + for (uint16_t index = 0; index < GetNumActiveReadHandlers(); index++) + { + ReadHandler * readHandler = ActiveHandlerAt(index); + if (readHandler == nullptr) + { + // Should not happen + continue; + } + + if (readHandler->IsType(ReadHandler::InteractionType::Subscribe)) + { + Access::SubjectDescriptor subject = readHandler->GetSubjectDescriptor(); + if (subject.fabricIndex != aFabricIndex) + { + continue; + } + + if (subject.authMode == Access::AuthMode::kCase) + { + if (subject.cats.CheckSubjectAgainstCATs(subjectID) || subjectID == subject.subject) + { + return readHandler->IsActiveSubscription(); + } + } + } + } + return false; +} + void InteractionModelEngine::ShutdownSubscriptions(FabricIndex aFabricIndex, NodeId aPeerNodeId) { assertChipStackLockedByCurrentThread(); diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index ca1fa92f240c58..39c86636aa9de5 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -306,6 +306,9 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler, CHIP_ERROR ResumeSubscriptions(); + // Return the state of a subscription matching a CAT Tag or a NodeId + bool IsMatchingSubscriptionActive(const FabricIndex & aFabricIndex, const uint64_t & nodeIdOrCAT); + #if CONFIG_BUILD_FOR_HOST_UNIT_TEST // // Get direct access to the underlying read handler pool diff --git a/src/app/icd/ICDManager.cpp b/src/app/icd/ICDManager.cpp index fd744a25764402..d1bc105830d2d0 100644 --- a/src/app/icd/ICDManager.cpp +++ b/src/app/icd/ICDManager.cpp @@ -28,6 +28,8 @@ #include #include +#include + #ifndef ICD_ENFORCE_SIT_SLOW_POLL_LIMIT // Set to 1 to enforce SIT Slow Polling Max value to 15seconds (spec 9.16.1.5) #define ICD_ENFORCE_SIT_SLOW_POLL_LIMIT 0 @@ -43,11 +45,12 @@ using namespace chip::app::Clusters::IcdManagement; static_assert(UINT8_MAX >= CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS, "ICDManager::mOpenExchangeContextCount cannot hold count for the max exchange count"); -void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeystore) +void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeystore, Messaging::ExchangeManager * exchangeManager) { VerifyOrDie(storage != nullptr); VerifyOrDie(fabricTable != nullptr); VerifyOrDie(symmetricKeystore != nullptr); + VerifyOrDie(exchangeManager != nullptr); bool supportLIT = SupportsFeature(Feature::kLongIdleTimeSupport); VerifyOrDieWithMsg((supportLIT == false) || SupportsFeature(Feature::kCheckInProtocolSupport), AppServer, @@ -63,6 +66,7 @@ void ICDManager::Init(PersistentStorageDelegate * storage, FabricTable * fabricT mFabricTable = fabricTable; VerifyOrDie(ICDNotifier::GetInstance().Subscribe(this) == CHIP_NO_ERROR); mSymmetricKeystore = symmetricKeystore; + mExchangeManager = exchangeManager; ICDManagementServer::GetInstance().SetSymmetricKeystore(mSymmetricKeystore); @@ -87,6 +91,7 @@ void ICDManager::Shutdown() mStorage = nullptr; mFabricTable = nullptr; mStateObserverPool.ReleaseAll(); + mICDSenderPool.ReleaseAll(); } bool ICDManager::SupportsFeature(Feature feature) @@ -101,6 +106,60 @@ bool ICDManager::SupportsFeature(Feature feature) #endif // !CONFIG_BUILD_FOR_HOST_UNIT_TEST } +void ICDManager::SendCheckInMsgs() +{ +#if !CONFIG_BUILD_FOR_HOST_UNIT_TEST + VerifyOrDie(mStorage != nullptr); + VerifyOrDie(mFabricTable != nullptr); + for (const auto & fabricInfo : *mFabricTable) + { + uint16_t supported_clients = ICDManagementServer::GetInstance().GetClientsSupportedPerFabric(); + + ICDMonitoringTable table(*mStorage, fabricInfo.GetFabricIndex(), supported_clients /*Table entry limit*/, + mSymmetricKeystore); + if (!table.IsEmpty()) + { + for (uint16_t i = 0; i < table.Limit(); i++) + { + ICDMonitoringEntry entry(mSymmetricKeystore); + CHIP_ERROR err = table.Get(i, entry); + if (err == CHIP_ERROR_NOT_FOUND) + { + break; + } + + if (err != CHIP_NO_ERROR) + { + // Try to fetch the next entry. + continue; + } + + bool active = + InteractionModelEngine::GetInstance()->IsMatchingSubscriptionActive(entry.fabricIndex, entry.monitoredSubject); + if (active) + { + continue; + } + + // SenderPool will be release upon transition from active to idle state + // This will happen when all ICD Check-In messages are sent on the network + ICDCheckInSender * sender = mICDSenderPool.CreateObject(mExchangeManager); + if (sender == nullptr) + { + ChipLogError(AppServer, "Failed to allocate ICDCheckinSender"); + return; + } + + if (CHIP_NO_ERROR != sender->RequestResolve(entry, mFabricTable)) + { + ChipLogError(AppServer, "Failed to send ICD Check-In"); + } + } + } + } +#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST +} + void ICDManager::UpdateICDMode() { assertChipStackLockedByCurrentThread(); @@ -166,6 +225,9 @@ void ICDManager::UpdateOperationState(OperationalState state) } #endif + // Going back to Idle, all check-in messages are sent + mICDSenderPool.ReleaseAll(); + CHIP_ERROR err = DeviceLayer::ConnectivityMgr().SetPollingInterval(slowPollInterval); if (err != CHIP_NO_ERROR) { @@ -202,6 +264,11 @@ void ICDManager::UpdateOperationState(OperationalState state) ChipLogError(AppServer, "Failed to set Fast Polling Interval: err %" CHIP_ERROR_FORMAT, err.Format()); } + if (SupportsFeature(Feature::kCheckInProtocolSupport)) + { + SendCheckInMsgs(); + } + postObserverEvent(ObserverEventType::EnterActiveMode); } else diff --git a/src/app/icd/ICDManager.h b/src/app/icd/ICDManager.h index 573a20011f7ffc..9a7b6bd15df465 100644 --- a/src/app/icd/ICDManager.h +++ b/src/app/icd/ICDManager.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include #include @@ -69,7 +70,7 @@ class ICDManager : public ICDListener }; ICDManager() {} - void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeyStore); + void Init(PersistentStorageDelegate * storage, FabricTable * fabricTable, Crypto::SymmetricKeystore * symmetricKeyStore, Messaging::ExchangeManager * exchangeManager); void Shutdown(); void UpdateICDMode(); void UpdateOperationState(OperationalState state); @@ -97,6 +98,7 @@ class ICDManager : public ICDListener void postObserverEvent(ObserverEventType event); ICDMode GetICDMode() { return mICDMode; } OperationalState GetOperationalState() { return mOperationalState; } + void SendCheckInMsgs(); static System::Clock::Milliseconds32 GetSITPollingThreshold() { return kSITPollingThreshold; } static System::Clock::Milliseconds32 GetFastPollingInterval() { return kFastPollingInterval; } @@ -148,14 +150,17 @@ class ICDManager : public ICDListener ICDMode mICDMode = ICDMode::SIT; PersistentStorageDelegate * mStorage = nullptr; FabricTable * mFabricTable = nullptr; + Messaging::ExchangeManager * mExchangeManager = nullptr; bool mTransitionToIdleCalled = false; Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr; ObjectPool mStateObserverPool; + ObjectPool mICDSenderPool; #ifdef CONFIG_BUILD_FOR_HOST_UNIT_TEST // feature map that can be changed at runtime for testing purposes uint32_t mFeatureMap = 0; #endif + }; } // namespace app diff --git a/src/app/server/Server.cpp b/src/app/server/Server.cpp index 65a45192728833..eab07e178b6e2f 100644 --- a/src/app/server/Server.cpp +++ b/src/app/server/Server.cpp @@ -255,15 +255,6 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) // This initializes clusters, so should come after lower level initialization. InitDataModelHandler(); -// ICD Init needs to be after data model init -#if CHIP_CONFIG_ENABLE_ICD_SERVER - mICDManager.Init(mDeviceStorage, &GetFabricTable(), mSessionKeystore); - // Register the ICDStateObservers. All observers are released at mICDManager.Shutdown() - // They can be released individually with ReleaseObserver - mICDManager.RegisterObserver(mReportScheduler); - mICDManager.RegisterObserver(&app::DnssdServer::Instance()); -#endif // CHIP_CONFIG_ENABLE_ICD_SERVER - #if defined(CHIP_APP_USE_ECHO) err = InitEchoHandler(&mExchangeMgr); SuccessOrExit(err); @@ -328,6 +319,15 @@ CHIP_ERROR Server::Init(const ServerInitParams & initParams) &mCASESessionManager, mSubscriptionResumptionStorage); SuccessOrExit(err); + // ICD Init needs to be after data model init and InteractionModel Init +#if CHIP_CONFIG_ENABLE_ICD_SERVER + mICDManager.Init(mDeviceStorage, &GetFabricTable(), mSessionKeystore, &mExchangeMgr); + // Register the ICDStateObservers. All observers are released at mICDManager.Shutdown() + // They can be released individually with ReleaseObserver + mICDManager.RegisterObserver(mReportScheduler); + mICDManager.RegisterObserver(&app::DnssdServer::Instance()); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER + // This code is necessary to restart listening to existing groups after a reboot // Each manufacturer needs to validate that they can rejoin groups by placing this code at the appropriate location for them //