diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 7fb4af5a23319a..c35de4bc95a86e 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -220,7 +220,7 @@ int main(int argc, char * argv[]) err = gSessionManager.Init(chip::kTestControllerNodeId, &chip::DeviceLayer::SystemLayer, &gTransportManager, &admins); SuccessOrExit(err); - err = gExchangeManager.Init(&gSessionManager); + err = gExchangeManager.Init(chip::kTestControllerNodeId, &gTransportManager, &gSessionManager); SuccessOrExit(err); err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager); diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index d0a18e6c1bad1a..bde7daf7610907 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -124,7 +124,7 @@ int main(int argc, char * argv[]) err = gSessionManager.Init(chip::kTestDeviceNodeId, &chip::DeviceLayer::SystemLayer, &gTransportManager, &admins); SuccessOrExit(err); - err = gExchangeManager.Init(&gSessionManager); + err = gExchangeManager.Init(chip::kTestDeviceNodeId, &gTransportManager, &gSessionManager); SuccessOrExit(err); err = chip::app::InteractionModelEngine::GetInstance()->Init(&gExchangeManager); diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 5f17dd341ba9c8..d09ff3f9e1a8be 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1261,6 +1261,37 @@ #define CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS 16 #endif // CHIP_CONFIG_MAX_EXCHANGE_CONTEXTS +/** + * @def CHIP_CONFIG_MAX_ACTIVE_CHANNELS + * + * @brief + * Maximum number of simultaneously active channels + */ +#ifndef CHIP_CONFIG_MAX_ACTIVE_CHANNELS +#define CHIP_CONFIG_MAX_ACTIVE_CHANNELS 16 +#endif // CHIP_CONFIG_MAX_ACTIVE_CHANNELS + +/** + * @def CHIP_CONFIG_MAX_CHANNEL_HANDLES + * + * @brief + * Maximum number of channel handles + */ +#ifndef CHIP_CONFIG_MAX_CHANNEL_HANDLES +#define CHIP_CONFIG_MAX_CHANNEL_HANDLES 64 +#endif // CHIP_CONFIG_MAX_CHANNEL_HANDLES + +/** + * @def CHIP_CONFIG_NODE_ADDRESS_RESOLVE_TIMEOUT_MSECS + * + * @brief + * This is the default timeout for node addres resolve over mDNS + * + */ +#ifndef CHIP_CONFIG_NODE_ADDRESS_RESOLVE_TIMEOUT_MSECS +#define CHIP_CONFIG_NODE_ADDRESS_RESOLVE_TIMEOUT_MSECS (5000) +#endif // CHIP_CONFIG_NODE_ADDRESS_RESOLVE_TIMEOUT_MSECS + /** * @def CHIP_CONFIG_CONNECT_IP_ADDRS * diff --git a/src/lib/core/CHIPError.cpp b/src/lib/core/CHIPError.cpp index cc7d6ccd9b14b0..ca77b500e63553 100644 --- a/src/lib/core/CHIPError.cpp +++ b/src/lib/core/CHIPError.cpp @@ -615,6 +615,9 @@ bool FormatCHIPError(char * buf, uint16_t bufSize, int32_t err) case CHIP_ERROR_IM_MALFORMED_STATUS_CODE: desc = "Malformed Interacton Model Status Code"; break; + case CHIP_ERROR_PEER_NODE_NOT_FOUND: + desc = "Unable to find the peer node"; + break; } #endif // !CHIP_CONFIG_SHORT_ERROR_STR diff --git a/src/lib/core/CHIPError.h b/src/lib/core/CHIPError.h index b9bf1f24b1e0a2..46630544f1227d 100644 --- a/src/lib/core/CHIPError.h +++ b/src/lib/core/CHIPError.h @@ -1761,6 +1761,14 @@ typedef CHIP_CONFIG_ERROR_TYPE CHIP_ERROR; */ #define CHIP_ERROR_IM_MALFORMED_STATUS_CODE _CHIP_ERROR(187) +/** + * @def CHIP_ERROR_PEER_NODE_NOT_FOUND + * + * @brief + * Unable to find the peer node + */ +#define CHIP_ERROR_PEER_NODE_NOT_FOUND _CHIP_ERROR(188) + /** * @} */ diff --git a/src/lib/core/tests/TestCHIPErrorStr.cpp b/src/lib/core/tests/TestCHIPErrorStr.cpp index 52aef711055da3..8d6d950eef03b1 100644 --- a/src/lib/core/tests/TestCHIPErrorStr.cpp +++ b/src/lib/core/tests/TestCHIPErrorStr.cpp @@ -225,6 +225,7 @@ static int32_t sContext[] = CHIP_ERROR_IM_MALFORMED_COMMAND_DATA_ELEMENT, CHIP_ERROR_IM_MALFORMED_EVENT_DATA_ELEMENT, CHIP_ERROR_IM_MALFORMED_STATUS_CODE, + CHIP_ERROR_PEER_NODE_NOT_FOUND, }; // clang-format on diff --git a/src/lib/support/Pool.h b/src/lib/support/Pool.h index 8fdee1e397ae44..29466b0206e88e 100644 --- a/src/lib/support/Pool.h +++ b/src/lib/support/Pool.h @@ -63,11 +63,7 @@ class StaticAllocatorBitmap : public StaticAllocatorBase void * Allocate(); void Deallocate(void * element); - // Test-only function declaration - template - void ForEachActiveObject(F f); - -private: +protected: void * At(size_t index) { return static_cast(mElements) + mElementSize * index; } size_t IndexOf(void * element) { @@ -119,6 +115,36 @@ class BitMapObjectPool : public StaticAllocatorBitmap Deallocate(element); } + /** + * @brief + * Run a functor for each active object in the pool + * + * @param f The functor of type `bool (*)(T*)`, return false to break the iteration + * @return bool Returns false if broke during iteration + * + * caution + * this function is not thread-safe, make sure all usage of the + * pool is protected by a lock, or else avoid using this function + */ + template + bool ForEachActiveObject(F f) + { + for (size_t word = 0; word * kBitChunkSize < Capacity(); ++word) + { + auto & usage = mUsage[word]; + auto value = usage.load(std::memory_order_relaxed); + for (size_t offset = 0; offset < kBitChunkSize && offset + word * kBitChunkSize < Capacity(); ++offset) + { + if ((value & (kBit1 << offset)) != 0) + { + if (!f(static_cast(At(word * kBitChunkSize + offset)))) + return false; + } + } + } + return true; + } + private: std::atomic mUsage[(N + kBitChunkSize - 1) / kBitChunkSize]; alignas(alignof(T)) uint8_t mMemory[N * sizeof(T)]; diff --git a/src/lib/support/tests/TestPool.cpp b/src/lib/support/tests/TestPool.cpp index 0c77c62b2d367e..257423c34f3be1 100644 --- a/src/lib/support/tests/TestPool.cpp +++ b/src/lib/support/tests/TestPool.cpp @@ -32,28 +32,14 @@ namespace chip { -template -void StaticAllocatorBitmap::ForEachActiveObject(F f) -{ - for (size_t word = 0; word * kBitChunkSize < Capacity(); ++word) - { - auto & usage = mUsage[word]; - auto value = usage.load(std::memory_order_relaxed); - for (size_t offset = 0; offset < kBitChunkSize && offset + word * kBitChunkSize < Capacity(); ++offset) - { - if ((value & (kBit1 << offset)) != 0) - { - f(At(word * kBitChunkSize + offset)); - } - } - } -} - template size_t GetNumObjectsInUse(BitMapObjectPool & pool) { size_t count = 0; - pool.ForEachActiveObject([&count](void *) { ++count; }); + pool.ForEachActiveObject([&count](void *) { + ++count; + return true; + }); return count; } diff --git a/src/messaging/BUILD.gn b/src/messaging/BUILD.gn index 766354d0a4ba70..941f3bca98e754 100644 --- a/src/messaging/BUILD.gn +++ b/src/messaging/BUILD.gn @@ -18,6 +18,10 @@ static_library("messaging") { output_name = "libMessagingLayer" sources = [ + "Channel.cpp", + "Channel.h", + "ChannelContext.cpp", + "ChannelContext.h", "ErrorCategory.cpp", "ErrorCategory.h", "ExchangeACL.h", diff --git a/src/messaging/Channel.cpp b/src/messaging/Channel.cpp new file mode 100644 index 00000000000000..24969831cf10e3 --- /dev/null +++ b/src/messaging/Channel.cpp @@ -0,0 +1,48 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#include +#include +#include + +namespace chip { +namespace Messaging { + +ChannelState ChannelHandle::GetState() const +{ + if (mAssociation == nullptr) + return ChannelState::kNone; + return mAssociation->mChannelContext->GetState(); +} + +ExchangeContext * ChannelHandle::NewExchange(ExchangeDelegate * delegate) +{ + assert(mAssociation != nullptr); + return mAssociation->mChannelContext->NewExchange(delegate); +} + +void ChannelHandle::Release() +{ + if (mAssociation == nullptr) + return; + + mAssociation->mChannelContext->mExchangeManager->ReleaseChannelHandle(mAssociation); + mAssociation = nullptr; +} + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/Channel.h b/src/messaging/Channel.h new file mode 100644 index 00000000000000..8cbd48a852c37e --- /dev/null +++ b/src/messaging/Channel.h @@ -0,0 +1,201 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file + * This file defines the API classes for to CHIP Channel. + * + * Channel is a object to abstract all low layer dependencies of an + * exchange, including secure session, transport connection and network + * status. + * + * Channel is not connection. Channel can abstract both connection + * oriented and connectionless transports, It contains information to send + * and receive messages via exchanges. For example, when using + * connectionless transport, channel will contain peer address and session + * key; when using connection oriented transport, channel will contain + * connection handle and session key. + * + * Channel is not session. Session do persistent through cold reboot, but + * channel doesn't. Applications must re-establish channels after a cold + * reboot. + * + * Because channel is a local concept, peer device is not able aware of + * channel establishment events. Instead, peer device is able to aware + * session establishment events, connection establishment events for + * connection oriented transport and message received events for + * connectionless transport. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace Messaging { + +/** + * @brief + * The ChannelBuilder object provides information to build a Channel. + * + * ChannelBuilder can be used by application to provide information to + * request a channel to a peer. When a channel is requested via + * ExchangeManager::EstablishChannel, a ChannelHandle will be return to + * represent the state of the channel + * + * The ChannelBuilder object should be short-live and it is only used within + * ExchangeManager::EstablishChannel call, after the call its state will be + * copied into an internal object represented by ChannelHandle, then the + * ChannelBuilder can be safely released. + */ +class ChannelBuilder +{ +public: + enum class TransportPreference + { + kConnectionless, + kPreferConnectionOriented, // will fallback to connectionless if TCP is not supported + kConnectionOriented, // will fail if TCP is not supported + + kDefault = kConnectionless, + }; + + ChannelBuilder & SetPeerNodeId(NodeId peerNodeId) + { + mPeerNodeId = peerNodeId; + return *this; + } + NodeId GetPeerNodeId() const { return mPeerNodeId; } + + ChannelBuilder & SetTransportPreference(TransportPreference preference) + { + mTransportPreference = preference; + return *this; + } + TransportPreference GetTransportPreference() const { return mTransportPreference; } + + uint16_t GetPeerKeyID() const { return mCaseParameters.mPeerKeyId; } + ChannelBuilder & SetPeerKeyID(uint16_t keyId) + { + mCaseParameters.mPeerKeyId = keyId; + return *this; + } + + Optional GetForcePeerAddress() const { return mForcePeerAddr; } + ChannelBuilder & SetForcePeerAddress(Inet::IPAddress peerAddr) + { + mForcePeerAddr.SetValue(peerAddr); + return *this; + } + +private: + NodeId mPeerNodeId = kUndefinedNodeId; + TransportPreference mTransportPreference = TransportPreference::kDefault; + struct + { + uint16_t mPeerKeyId; + } mCaseParameters; + + Optional mForcePeerAddr; +}; + +class ExchangeContext; +class ExchangeDelegate; + +enum class ChannelState +{ + kNone, + kPreparing, + kReady, + kClosed, + kFailed, +}; + +class ChannelContextHandleAssociation; + +/** + * @brief + * ChannelHandle is a reference to a channel. An active ChannelHandle will + * keep the channel available and ready for use, such that a message can be + * sent immediately to the peer. + * + * The ChannelHandle controls the lifespan of the channel. When the handle + * is released, the channel will be flagged as pending close, and if there + * is no active exchange which is using the channel, the channel will be + * closed. + * + * The ChannelHandle will track channel status, and notify applications + * when the channel state changes via ChannelDelegate. + */ +class ChannelHandle +{ +public: + explicit ChannelHandle(ChannelContextHandleAssociation * association = nullptr) : mAssociation(association) {} + ~ChannelHandle() { Release(); } + + // non copyable + ChannelHandle(const ChannelHandle &) = delete; + ChannelHandle & operator=(const ChannelHandle &) = delete; + + // movable + ChannelHandle(ChannelHandle && that) + { + Release(); + this->mAssociation = that.mAssociation; + that.mAssociation = nullptr; + } + ChannelHandle & operator=(ChannelHandle && that) + { + Release(); + this->mAssociation = that.mAssociation; + that.mAssociation = nullptr; + return *this; + } + + ChannelState GetState() const; + + /* + * @brief + * Create a new exchange on the channel. + * + * @pre GetState() == ChannelState::kReady + */ + ExchangeContext * NewExchange(ExchangeDelegate * delegate); + + void Release(); + +private: + ChannelContextHandleAssociation * mAssociation; +}; + +/** + * @brief + * Callback receiver interface of channels + */ +class ChannelDelegate +{ +public: + virtual ~ChannelDelegate() {} + + virtual void OnEstablished() = 0; + virtual void OnClosed() = 0; + virtual void OnFail(CHIP_ERROR err) = 0; +}; + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/ChannelContext.cpp b/src/messaging/ChannelContext.cpp new file mode 100644 index 00000000000000..958de27f2216fb --- /dev/null +++ b/src/messaging/ChannelContext.cpp @@ -0,0 +1,373 @@ +/* + * + * Copyright (c) 2021 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. + */ + +#include +#include +#include + +namespace chip { +namespace Messaging { + +void ChannelContextDeletor::Release(ChannelContext * context) +{ + context->mExchangeManager->ReleaseChannelContext(context); +} + +void ChannelContext::Start(const ChannelBuilder & builder) +{ + if (mState != ChannelState::kNone) + return; + EnterPreparingState(builder); +} + +ExchangeContext * ChannelContext::NewExchange(ExchangeDelegate * delegate) +{ + assert(GetState() == ChannelState::kReady); + return mExchangeManager->NewContext(mStateVars.mReady.mSession, delegate); +} + +bool ChannelContext::MatchNodeId(NodeId nodeId) +{ + switch (mState) + { + case ChannelState::kPreparing: + return nodeId == mStateVars.mPreparing.mBuilder.GetPeerNodeId(); + case ChannelState::kReady: { + auto state = mExchangeManager->GetSessionMgr()->GetPeerConnectionState(mStateVars.mReady.mSession); + if (state == nullptr) + return false; + return nodeId == state->GetPeerNodeId(); + } + default: + return false; + } +} + +bool ChannelContext::MatchTransport(Transport::Type transport) +{ + switch (mState) + { + case ChannelState::kPreparing: + switch (mStateVars.mPreparing.mBuilder.GetTransportPreference()) + { + case ChannelBuilder::TransportPreference::kPreferConnectionOriented: + case ChannelBuilder::TransportPreference::kConnectionOriented: + return transport == Transport::Type::kTcp; + case ChannelBuilder::TransportPreference::kConnectionless: + return transport == Transport::Type::kUdp; + } + return false; + case ChannelState::kReady: { + auto state = mExchangeManager->GetSessionMgr()->GetPeerConnectionState(mStateVars.mReady.mSession); + if (state == nullptr) + return false; + return transport == state->GetPeerAddress().GetTransportType(); + } + default: + return false; + } +} + +bool ChannelContext::MatchTransportPreference(ChannelBuilder::TransportPreference transport) +{ + switch (transport) + { + case ChannelBuilder::TransportPreference::kPreferConnectionOriented: + case ChannelBuilder::TransportPreference::kConnectionOriented: + return MatchTransport(Transport::Type::kTcp); + case ChannelBuilder::TransportPreference::kConnectionless: + return MatchTransport(Transport::Type::kUdp); + } + return false; +} + +bool ChannelContext::MatchCaseParameters() +{ + // TODO: not supported yet, should compare CASE parameters here, and return false if doesn't match + return true; +} + +bool ChannelContext::MatchesBuilder(const ChannelBuilder & builder) +{ + // Channel is identified by {node id, {case key id}, network interface, tcp/udp} + // PASE is not supported yet + // Network interface is not supported yet + // Channel is reused if builder parameters are matching an existing channel + if (!MatchNodeId(builder.GetPeerNodeId())) + return false; + if (!MatchCaseParameters()) + return false; + + return MatchTransportPreference(builder.GetTransportPreference()); +} + +bool ChannelContext::IsCasePairing() +{ + return mState == ChannelState::kPreparing && mStateVars.mPreparing.mState == PrepareState::kCasePairing; +} + +bool ChannelContext::MatchesSession(SecureSessionHandle session, SecureSessionMgr * ssm) +{ + switch (mState) + { + case ChannelState::kPreparing: { + switch (mStateVars.mPreparing.mState) + { + case PrepareState::kCasePairing: { + auto state = ssm->GetPeerConnectionState(session); + return (state->GetPeerNodeId() == mStateVars.mPreparing.mBuilder.GetPeerNodeId() && + state->GetPeerKeyID() == mStateVars.mPreparing.mBuilder.GetPeerKeyID()); + } + default: + return false; + } + } + case ChannelState::kReady: + return mStateVars.mReady.mSession == session; + default: + return false; + } +} + +void ChannelContext::EnterPreparingState(const ChannelBuilder & builder) +{ + mState = ChannelState::kPreparing; + mStateVars.mPreparing.mBuilder = builder; + + EnterAddressResolve(); +} + +void ChannelContext::ExitPreparingState() {} + +// Address resolve +void ChannelContext::EnterAddressResolve() +{ + mStateVars.mPreparing.mState = PrepareState::kAddressResolving; + + // Skip address resolve if the address is provided + { + auto addr = mStateVars.mPreparing.mBuilder.GetForcePeerAddress(); + if (addr.HasValue()) + { + mStateVars.mPreparing.mAddress = addr.Value(); + ExitAddressResolve(); + // Only CASE session is supported + EnterCasePairingState(); + return; + } + } + + // TODO: call mDNS Scanner::SubscribeNode after PR #4459 is ready + // Scanner::RegisterScannerDelegate(this) + // Scanner::SubscribeNode(mStateVars.mPreparing.mBuilder.GetPeerNodeId()) + + // The HandleNodeIdResolve may already have been called, recheck the state here before set up the timer + if (mState == ChannelState::kPreparing && mStateVars.mPreparing.mState == PrepareState::kAddressResolving) + { + System::Layer * layer = mExchangeManager->GetSessionMgr()->SystemLayer(); + layer->StartTimer(CHIP_CONFIG_NODE_ADDRESS_RESOLVE_TIMEOUT_MSECS, AddressResolveTimeout, this); + Retain(); // Keep the pointer in the timer + } +} + +void ChannelContext::AddressResolveTimeout(System::Layer * aLayer, void * aAppState, System::Error aError) +{ + ChannelContext * me = static_cast(aAppState); + me->AddressResolveTimeout(); + me->Release(); +} + +void ChannelContext::AddressResolveTimeout() +{ + if (mState != ChannelState::kPreparing) + return; + if (mStateVars.mPreparing.mState != PrepareState::kAddressResolving) + return; + + ExitAddressResolve(); + ExitPreparingState(); + EnterFailedState(CHIP_ERROR_PEER_NODE_NOT_FOUND); +} + +void ChannelContext::HandleNodeIdResolve(CHIP_ERROR error, uint64_t nodeId, const Mdns::MdnsService & address) +{ + switch (mState) + { + case ChannelState::kReady: { + if (error != CHIP_NO_ERROR) + { + // Ignore mDNS fail in ready state + return; + } + + // TODO: adjust peer address, secure session manager is not able to change peer address. + return; + } + case ChannelState::kPreparing: { + switch (mStateVars.mPreparing.mState) + { + case PrepareState::kAddressResolving: { + if (error != CHIP_NO_ERROR) + { + ExitAddressResolve(); + ExitPreparingState(); + EnterFailedState(error); + return; + } + + if (!address.mAddress.HasValue()) + return; + mStateVars.mPreparing.mAddressType = address.mAddressType; + mStateVars.mPreparing.mAddress = address.mAddress.Value(); + ExitAddressResolve(); + EnterCasePairingState(); + return; + } + case PrepareState::kCasePairing: + case PrepareState::kCasePairingDone: + return; + } + return; + } + case ChannelState::kNone: + case ChannelState::kClosed: + case ChannelState::kFailed: + return; + } +} + +// Session establishment +CHIP_ERROR ChannelContext::SendSessionEstablishmentMessage(const PacketHeader & header, const Transport::PeerAddress & peerAddress, + System::PacketBufferHandle msgIn) +{ + return mExchangeManager->GetTransportManager()->SendMessage(header, peerAddress, std::move(msgIn)); +} + +CHIP_ERROR ChannelContext::HandlePairingMessage(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress, + System::PacketBufferHandle && msg) +{ + if (IsCasePairing()) + return mStateVars.mPreparing.mCasePairingSession->HandlePeerMessage(packetHeader, peerAddress, std::move(msg)); + return CHIP_ERROR_INCORRECT_STATE; +} + +void ChannelContext::EnterCasePairingState() +{ + mStateVars.mPreparing.mState = PrepareState::kCasePairing; + mStateVars.mPreparing.mCasePairingSession = Platform::New(); + // TODO: currently only supports IP/UDP paring + Transport::PeerAddress addr; + addr.SetTransportType(Transport::Type::kUdp).SetIPAddress(mStateVars.mPreparing.mAddress); + CHIP_ERROR err = mStateVars.mPreparing.mCasePairingSession->EstablishSession(addr, mExchangeManager->GetLocalNodeId(), + mStateVars.mPreparing.mBuilder.GetPeerNodeId(), + mExchangeManager->GetNextKeyId(), this); + if (err != CHIP_NO_ERROR) + { + ExitCasePairingState(); + ExitPreparingState(); + EnterFailedState(err); + } +} + +void ChannelContext::ExitCasePairingState() +{ + Platform::Delete(mStateVars.mPreparing.mCasePairingSession); +} + +void ChannelContext::OnSessionEstablishmentError(CHIP_ERROR error) +{ + if (mState != ChannelState::kPreparing) + return; + switch (mStateVars.mPreparing.mState) + { + case PrepareState::kCasePairing: + ExitCasePairingState(); + ExitPreparingState(); + EnterFailedState(error); + return; + default: + return; + } +} + +void ChannelContext::OnSessionEstablished() +{ + if (mState != ChannelState::kPreparing) + return; + switch (mStateVars.mPreparing.mState) + { + case PrepareState::kCasePairing: + ExitCasePairingState(); + mStateVars.mPreparing.mState = PrepareState::kCasePairingDone; + // TODO: current CASE paring session API doesn't show how to derive a secure session + return; + default: + return; + } +} + +void ChannelContext::OnNewConnection(SecureSessionHandle session) +{ + if (mState != ChannelState::kPreparing) + return; + if (mStateVars.mPreparing.mState != PrepareState::kCasePairingDone) + return; + + ExitPreparingState(); + EnterReadyState(session); +} + +void ChannelContext::EnterReadyState(SecureSessionHandle session) +{ + mState = ChannelState::kReady; + + mStateVars.mReady.mSession = session; + mExchangeManager->NotifyChannelEvent(this, [](ChannelDelegate * delegate) { delegate->OnEstablished(); }); +} + +void ChannelContext::OnConnectionExpired(SecureSessionHandle session) +{ + if (mState != ChannelState::kReady) + return; + + ExitReadyState(); + EnterClosedState(); +} + +void ChannelContext::ExitReadyState() +{ + // TODO: close sesure session + // Currently SecureSessionManager doesn't provide an interface to close a session + + // TODO: call mDNS Scanner::UnubscribeNode after PR #4459 is ready + // Scanner::UnsubscribeNode(mStateVars.mPreparing.mBuilder.GetPeerNodeId()) +} + +void ChannelContext::EnterFailedState(CHIP_ERROR error) +{ + mState = ChannelState::kFailed; + mExchangeManager->NotifyChannelEvent(this, [error](ChannelDelegate * delegate) { delegate->OnFail(error); }); +} + +void ChannelContext::EnterClosedState() +{ + mState = ChannelState::kClosed; + mExchangeManager->NotifyChannelEvent(this, [](ChannelDelegate * delegate) { delegate->OnClosed(); }); +} + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/ChannelContext.h b/src/messaging/ChannelContext.h new file mode 100644 index 00000000000000..a3c84880b88fba --- /dev/null +++ b/src/messaging/ChannelContext.h @@ -0,0 +1,190 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file + * This file defines the internal classes used by CHIP Channel. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Messaging { + +class ExchangeManager; +class ChannelContext; + +class ChannelContextDeletor +{ +public: + static void Release(ChannelContext * context); +}; + +/** + * @brief + * The object of the class holds all state of a channel. It is a state machine, with following states: + * + * N: None, the initial state + * P: Preparing + * R: Ready, the channel is ready to use + * C: Closed, the channel is closed + * F: Failed, the channel is failed + * + * +---+ +---+ +---+ +---+ + * | N |-->| P |-->| R |-->| C | + * +---+ +---+ +---+ +---+ + * | | + * | | +---+ + * +-------+---->| F | + * +---+ + * + * Note: The state never goes back, when a channel is failed, it can't be reset or fixed. The application must create a + * new channel to replace the failed channel + * + * Preparing Substates: + * A: AddressResolving, use mDNS to resolve the node address + * C: CasePairing, do SIGMA key exchange + * CD: CasePairingDone, wait for OnNewConnection from SecureSessionManager + * + * /---\ +---+ +---+ +----+ /---\ + * | |-->| A |-->| C |-->| CD |-->| O | + * \---/ +---+ +---+ +----+ \---/ + */ +class ChannelContext : public ReferenceCounted, public SessionEstablishmentDelegate +{ +public: + ChannelContext(ExchangeManager * exchangeManager) : mState(ChannelState::kNone), mExchangeManager(exchangeManager) {} + + void Start(const ChannelBuilder & builder); + + /* + * @brief + * Create a new exchange on the channel. + * + * @pre GetState() == ChannelState::kReady + */ + ExchangeContext * NewExchange(ExchangeDelegate * delegate); + + ChannelState GetState() const { return mState; } + + bool MatchNodeId(NodeId nodeId); + bool MatchTransport(Transport::Type transport); + bool MatchTransportPreference(ChannelBuilder::TransportPreference transport); + bool MatchCaseParameters(); + + bool IsCasePairing(); + + bool MatchesBuilder(const ChannelBuilder & builder); + bool MatchesSession(SecureSessionHandle session, SecureSessionMgr * ssm); + + // events of ResolveDelegate, propagated from ExchangeManager + void HandleNodeIdResolve(CHIP_ERROR error, uint64_t nodeId, const Mdns::MdnsService & address); + + // events of SecureSessionManager, propagated from ExchangeManager + void OnNewConnection(SecureSessionHandle session); + void OnConnectionExpired(SecureSessionHandle session); + + // Pairing callbacks + CHIP_ERROR HandlePairingMessage(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress, + System::PacketBufferHandle && msg); + CHIP_ERROR SendSessionEstablishmentMessage(const PacketHeader & header, const Transport::PeerAddress & peerAddress, + System::PacketBufferHandle msgIn) override; + void OnSessionEstablishmentError(CHIP_ERROR error) override; + void OnSessionEstablished() override; + +private: + friend class ChannelContextDeletor; + friend class ChannelHandle; + + ChannelState mState; + ExchangeManager * mExchangeManager; + + enum class PrepareState + { + kAddressResolving, + kCasePairing, + kCasePairingDone, + }; + + union StateVars + { + StateVars() {} + + // mPreparing is pretty big, consider move it outside + struct PrepareVars + { + PrepareState mState; + Inet::IPAddressType mAddressType; + Inet::IPAddress mAddress; + CASESession * mCasePairingSession; + ChannelBuilder mBuilder; + } mPreparing; + + struct ReadyVars + { + SecureSessionHandle mSession; + } mReady; + } mStateVars; + + // State machine functions + void EnterPreparingState(const ChannelBuilder & builder); + void ExitPreparingState(); + + void EnterReadyState(SecureSessionHandle session); + void ExitReadyState(); + + void EnterFailedState(CHIP_ERROR error); + void EnterClosedState(); + + // Preparing sub-states + void EnterAddressResolve(); + static void AddressResolveTimeout(System::Layer * aLayer, void * aAppState, System::Error aError); + void AddressResolveTimeout(); + void ExitAddressResolve() {} + + void EnterCasePairingState(); + void ExitCasePairingState(); +}; + +class ChannelContextHandleAssociation +{ +public: + ChannelContextHandleAssociation(ChannelContext * channelContext, ChannelDelegate * channelDelegate) : + mChannelContext(channelContext), mChannelDelegate(channelDelegate) + { + mChannelContext->Retain(); + } + ~ChannelContextHandleAssociation() { mChannelContext->Release(); } + +private: + friend class ExchangeManager; + friend class ChannelHandle; + ChannelContext * mChannelContext; + ChannelDelegate * mChannelDelegate; +}; + +} // namespace Messaging +} // namespace chip diff --git a/src/messaging/ExchangeMgr.cpp b/src/messaging/ExchangeMgr.cpp index a22052dfff7c36..ba544d1965c279 100644 --- a/src/messaging/ExchangeMgr.cpp +++ b/src/messaging/ExchangeMgr.cpp @@ -65,20 +65,24 @@ ExchangeManager::ExchangeManager() : mReliableMessageMgr(mContextPool) mState = State::kState_NotInitialized; } -CHIP_ERROR ExchangeManager::Init(SecureSessionMgr * sessionMgr) +CHIP_ERROR ExchangeManager::Init(NodeId localNodeId, TransportMgrBase * transportMgr, SecureSessionMgr * sessionMgr) { CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrReturnError(mState == State::kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE); - mSessionMgr = sessionMgr; + mLocalNodeId = localNodeId; + mTransportMgr = transportMgr; + mSessionMgr = sessionMgr; mNextExchangeId = GetRandU16(); + mNextKeyId = 0; mContextsInUse = 0; memset(UMHandlerPool, 0, sizeof(UMHandlerPool)); - OnExchangeContextChanged = nullptr; + + mTransportMgr->SetRendezvousSession(this); sessionMgr->SetDelegate(this); @@ -103,8 +107,6 @@ CHIP_ERROR ExchangeManager::Shutdown() mSessionMgr = nullptr; } - OnExchangeContextChanged = nullptr; - mState = State::kState_NotInitialized; return CHIP_NO_ERROR; @@ -351,6 +353,50 @@ void ExchangeManager::OnMessageReceived(const PacketHeader & packetHeader, const } } +ChannelHandle ExchangeManager::EstablishChannel(const ChannelBuilder & builder, ChannelDelegate * delegate) +{ + ChannelContext * channelContext = nullptr; + + // Find an existing Channel matching the builder + mChannelContexts.ForEachActiveObject([&](ChannelContext * context) { + if (context->MatchesBuilder(builder)) + { + channelContext = context; + return false; + } + return true; + }); + + if (channelContext == nullptr) + { + // create a new channel if not found + channelContext = mChannelContexts.CreateObject(this); + if (channelContext == nullptr) + return ChannelHandle{ nullptr }; + channelContext->Start(builder); + } + else + { + channelContext->Retain(); + } + + ChannelContextHandleAssociation * association = mChannelHandles.CreateObject(channelContext, delegate); + channelContext->Release(); + return ChannelHandle{ association }; +} + +void ExchangeManager::OnNewConnection(SecureSessionHandle session, SecureSessionMgr * mgr) +{ + mChannelContexts.ForEachActiveObject([&](ChannelContext * context) { + if (context->MatchesSession(session, mgr)) + { + context->OnNewConnection(session); + return false; + } + return true; + }); +} + void ExchangeManager::OnConnectionExpired(SecureSessionHandle session, SecureSessionMgr * mgr) { for (auto & ec : mContextPool) @@ -361,6 +407,50 @@ void ExchangeManager::OnConnectionExpired(SecureSessionHandle session, SecureSes // Continue iterate because there can be multiple contexts associated with the connection. } } + + mChannelContexts.ForEachActiveObject([&](ChannelContext * context) { + if (context->MatchesSession(session, mgr)) + { + context->OnConnectionExpired(session); + return false; + } + return true; + }); +} + +void ExchangeManager::OnMessageReceived(const PacketHeader & header, const Transport::PeerAddress & source, + System::PacketBufferHandle msgBuf) +{ + auto peer = header.GetSourceNodeId(); + if (!peer.HasValue()) + { + char addrBuffer[Transport::PeerAddress::kMaxToStringSize]; + source.ToString(addrBuffer, sizeof(addrBuffer)); + ChipLogError(ExchangeManager, "Unencrypted message from %s is dropped since no source node id in packet header.", + addrBuffer); + return; + } + + auto node = peer.Value(); + auto notFound = mChannelContexts.ForEachActiveObject([&](ChannelContext * context) { + if (context->IsCasePairing() && context->MatchNodeId(node)) + { + CHIP_ERROR err = context->HandlePairingMessage(header, source, std::move(msgBuf)); + if (err != CHIP_NO_ERROR) + ChipLogError(ExchangeManager, "HandlePairingMessage error %s from node %llu.", chip::ErrorStr(err), node); + return false; + } + return true; + }); + + if (notFound) + { + char addrBuffer[Transport::PeerAddress::kMaxToStringSize]; + source.ToString(addrBuffer, sizeof(addrBuffer)); + ChipLogError(ExchangeManager, "Unencrypted message from %s is dropped since no session found for node %llu.", addrBuffer, + node); + return; + } } void ExchangeManager::IncrementContextsInUse() diff --git a/src/messaging/ExchangeMgr.h b/src/messaging/ExchangeMgr.h index 33a8a8c287f333..45c71948497168 100644 --- a/src/messaging/ExchangeMgr.h +++ b/src/messaging/ExchangeMgr.h @@ -26,11 +26,15 @@ #include +#include +#include #include #include #include #include +#include #include +#include namespace chip { namespace Messaging { @@ -46,7 +50,7 @@ static constexpr int16_t kAnyMessageType = -1; * It works on be behalf of higher layers, creating ExchangeContexts and * handling the registration/unregistration of unsolicited message handlers. */ -class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate +class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate, public TransportMgrDelegate { public: ExchangeManager(); @@ -66,7 +70,7 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate * @retval #CHIP_NO_ERROR On success. * */ - CHIP_ERROR Init(SecureSessionMgr * sessionMgr); + CHIP_ERROR Init(NodeId localNodeId, TransportMgrBase * transportMgr, SecureSessionMgr * sessionMgr); /** * Shutdown the ExchangeManager. This terminates this instance @@ -184,15 +188,37 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate void HandleGroupMessageReceived(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, const SecureSessionHandle & session, System::PacketBufferHandle msgBuf); + // Channel public APIs + ChannelHandle EstablishChannel(const ChannelBuilder & builder, ChannelDelegate * delegate); + + // Internal APIs used for channel + void ReleaseChannelContext(ChannelContext * channel) { mChannelContexts.ReleaseObject(channel); } + + void ReleaseChannelHandle(ChannelContextHandleAssociation * association) { mChannelHandles.ReleaseObject(association); } + + template + void NotifyChannelEvent(ChannelContext * channel, Event event) + { + mChannelHandles.ForEachActiveObject([&](ChannelContextHandleAssociation * association) { + if (association->mChannelContext == channel) + event(association->mChannelDelegate); + return true; + }); + } + void IncrementContextsInUse(); void DecrementContextsInUse(); + TransportMgrBase * GetTransportManager() const { return mTransportMgr; } SecureSessionMgr * GetSessionMgr() const { return mSessionMgr; } ReliableMessageMgr * GetReliableMessageMgr() { return &mReliableMessageMgr; }; MessageCounterSyncMgr * GetMessageCounterSyncMgr() { return &mMessageCounterSyncMgr; }; + Transport::AdminId GetAdminId() { return mAdminId; } + NodeId GetLocalNodeId() { return mLocalNodeId; } + uint16_t GetNextKeyId() { return ++mNextKeyId; } size_t GetContextsInUse() const { return mContextsInUse; } private: @@ -209,17 +235,23 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate int16_t MessageType; }; + NodeId mLocalNodeId; // < Id of the current node uint16_t mNextExchangeId; + uint16_t mNextKeyId; State mState; + TransportMgrBase * mTransportMgr; SecureSessionMgr * mSessionMgr; ReliableMessageMgr mReliableMessageMgr; MessageCounterSyncMgr mMessageCounterSyncMgr; + Transport::AdminId mAdminId = 0; + std::array mContextPool; size_t mContextsInUse; UnsolicitedMessageHandler UMHandlerPool[CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS]; - void (*OnExchangeContextChanged)(size_t numContextsInUse); + BitMapObjectPool mChannelContexts; + BitMapObjectPool mChannelHandles; ExchangeContext * AllocContext(uint16_t ExchangeId, SecureSessionHandle session, bool Initiator, ExchangeDelegate * delegate); @@ -233,7 +265,12 @@ class DLL_EXPORT ExchangeManager : public SecureSessionMgrDelegate void OnMessageReceived(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, SecureSessionHandle session, System::PacketBufferHandle msgBuf, SecureSessionMgr * msgLayer) override; + void OnNewConnection(SecureSessionHandle session, SecureSessionMgr * mgr) override; void OnConnectionExpired(SecureSessionHandle session, SecureSessionMgr * mgr) override; + + // TransportMgrDelegate interface for rendezvous sessions + void OnMessageReceived(const PacketHeader & header, const Transport::PeerAddress & source, + System::PacketBufferHandle msgBuf) override; }; } // namespace Messaging diff --git a/src/messaging/tests/BUILD.gn b/src/messaging/tests/BUILD.gn index c7bc8ee2e387d2..7191349fdfc50f 100644 --- a/src/messaging/tests/BUILD.gn +++ b/src/messaging/tests/BUILD.gn @@ -25,6 +25,7 @@ chip_test_suite("tests") { sources = [ "MessagingContext.cpp", "MessagingContext.h", + "TestChannel.cpp", "TestExchangeMgr.cpp", "TestMessageCounterSyncMgr.cpp", "TestMessagingLayer.h", @@ -46,6 +47,7 @@ chip_test_suite("tests") { ] tests = [ + "TestChannel", "TestExchangeMgr", "TestMessageCounterSyncMgr", "TestReliableMessageProtocol", diff --git a/src/messaging/tests/MessagingContext.cpp b/src/messaging/tests/MessagingContext.cpp index ac73bfbe92c7c5..88de214292dccf 100644 --- a/src/messaging/tests/MessagingContext.cpp +++ b/src/messaging/tests/MessagingContext.cpp @@ -38,7 +38,7 @@ CHIP_ERROR MessagingContext::Init(nlTestSuite * suite, TransportMgrBase * transp ReturnErrorOnFailure(mSecureSessionMgr.Init(GetSourceNodeId(), &GetSystemLayer(), transport, &mAdmins)); - ReturnErrorOnFailure(mExchangeManager.Init(&mSecureSessionMgr)); + ReturnErrorOnFailure(mExchangeManager.Init(GetSourceNodeId(), transport, &mSecureSessionMgr)); ReturnErrorOnFailure(mSecureSessionMgr.NewPairing(mPeer, GetDestinationNodeId(), &mPairingLocalToPeer, SecureSessionMgr::PairingDirection::kInitiator, mSrcAdminId)); diff --git a/src/messaging/tests/TestChannel.cpp b/src/messaging/tests/TestChannel.cpp new file mode 100644 index 00000000000000..ef5c5a5018df52 --- /dev/null +++ b/src/messaging/tests/TestChannel.cpp @@ -0,0 +1,200 @@ +/* + * + * Copyright (c) 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 implements unit tests for the ExchangeManager implementation. + */ + +#include "TestMessagingLayer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace { + +using namespace chip; +using namespace chip::Inet; +using namespace chip::Transport; +using namespace chip::Messaging; + +using TestContext = chip::Test::MessagingContext; + +TestContext sContext; + +class LoopbackTransport : public Transport::Base +{ +public: + /// Transports are required to have a constructor that takes exactly one argument + CHIP_ERROR Init(const char * unused) { return CHIP_NO_ERROR; } + + CHIP_ERROR SendMessage(const PacketHeader & header, const PeerAddress & address, System::PacketBufferHandle msgBuf) override + { + HandleMessageReceived(header, address, std::move(msgBuf)); + return CHIP_NO_ERROR; + } + + bool CanSendToPeer(const PeerAddress & address) override { return true; } +}; + +TransportMgr gTransportMgr; + +class MockAppDelegate : public ExchangeDelegate +{ +public: + void OnMessageReceived(ExchangeContext * ec, const PacketHeader & packetHeader, const PayloadHeader & payloadHeader, + System::PacketBufferHandle buffer) override + { + IsOnMessageReceivedCalled = true; + } + + void OnResponseTimeout(ExchangeContext * ec) override {} + + bool IsOnMessageReceivedCalled = false; +}; + +class MockChannelDelegate : public ChannelDelegate +{ +public: + ~MockChannelDelegate() override {} + + void OnEstablished() override {} + void OnClosed() override {} + void OnFail(CHIP_ERROR err) override {} +}; + +void CheckExchangeChannels(nlTestSuite * inSuite, void * inContext) +{ + TestContext & ctx = *reinterpret_cast(inContext); + + // create unsolicited exchange + MockAppDelegate mockUnsolicitedAppDelegate; + CHIP_ERROR err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(0x0001, 0x0001, &mockUnsolicitedAppDelegate); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + // create the channel + ChannelBuilder channelBuilder; + channelBuilder.SetPeerNodeId(ctx.GetDestinationNodeId()).SetForcePeerAddress(ctx.GetAddress()); + MockChannelDelegate channelDelegate; + auto channelHandle = ctx.GetExchangeManager().EstablishChannel(channelBuilder, &channelDelegate); + return; + +#if 0 + // TODO: complete test when CASESession is completed + // wait for channel establishment + ctx.DriveIOUntil(1000, [&] { return channelHandle.GetState() == ChannelState::kReady; }); + NL_TEST_ASSERT(inSuite, channelHandle.GetState() == ChannelState::kReady); + + MockAppDelegate mockAppDelegate; + ExchangeContext * ec1 = channelHandle.NewExchange(&mockAppDelegate); + + // send a malicious packet + ec1->SendMessage(0x0001, 0x0002, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), + SendFlags(Messaging::SendMessageFlags::kNone)); + NL_TEST_ASSERT(inSuite, !mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled); + + // send a good packet + ec1->SendMessage(0x0001, 0x0001, System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize), + SendFlags(Messaging::SendMessageFlags::kNone)); + NL_TEST_ASSERT(inSuite, mockUnsolicitedAppDelegate.IsOnMessageReceivedCalled); + + ec1->Close(); + channelHandle.Release(); +#endif +} + +// Test Suite + +/** + * Test Suite that lists all the test functions. + */ +// clang-format off +const nlTest sTests[] = +{ + NL_TEST_DEF("Test Channel/Exchange", CheckExchangeChannels), + + NL_TEST_SENTINEL() +}; +// clang-format on + +int Initialize(void * aContext); +int Finalize(void * aContext); + +// clang-format off +nlTestSuite sSuite = +{ + "Test-CHIP-ExchangeManager", + &sTests[0], + Initialize, + Finalize +}; +// clang-format on + +/** + * Initialize the test suite. + */ +int Initialize(void * aContext) +{ + CHIP_ERROR err = chip::Platform::MemoryInit(); + if (err != CHIP_NO_ERROR) + return FAILURE; + + err = gTransportMgr.Init("LOOPBACK"); + if (err != CHIP_NO_ERROR) + return FAILURE; + + err = reinterpret_cast(aContext)->Init(&sSuite, &gTransportMgr); + return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE; +} + +/** + * Finalize the test suite. + */ +int Finalize(void * aContext) +{ + CHIP_ERROR err = reinterpret_cast(aContext)->Shutdown(); + chip::Platform::MemoryShutdown(); + return (err == CHIP_NO_ERROR) ? SUCCESS : FAILURE; +} + +} // namespace + +/** + * Main + */ +int TestChannel() +{ + // Run test suit against one context + nlTestRunner(&sSuite, &sContext); + + return (nlTestRunnerStats(&sSuite)); +} diff --git a/src/messaging/tests/TestChannelDriver.cpp b/src/messaging/tests/TestChannelDriver.cpp new file mode 100644 index 00000000000000..f8c2f7dd180d76 --- /dev/null +++ b/src/messaging/tests/TestChannelDriver.cpp @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2021 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. + */ + +/** + * @file + * This file implements a standalone/native program executable + * test driver for the CHIP core library CHIP ExchangeManager tests. + * + */ + +#include "TestMessagingLayer.h" + +#include + +int main() +{ + // Generate machine-readable, comma-separated value (CSV) output. + nlTestSetOutputStyle(OUTPUT_CSV); + + return (TestChannel()); +} diff --git a/src/messaging/tests/TestMessagingLayer.h b/src/messaging/tests/TestMessagingLayer.h index fbab413701831b..80b55b919bf3d1 100644 --- a/src/messaging/tests/TestMessagingLayer.h +++ b/src/messaging/tests/TestMessagingLayer.h @@ -28,6 +28,7 @@ extern "C" { #endif +int TestChannel(); int TestExchangeMgr(void); int TestMessageCounterSyncMgr(void); int TestReliableMessageProtocol(void); diff --git a/src/messaging/tests/echo/echo_requester.cpp b/src/messaging/tests/echo/echo_requester.cpp index ef75ec11b90d4c..275d39dc0d8921 100644 --- a/src/messaging/tests/echo/echo_requester.cpp +++ b/src/messaging/tests/echo/echo_requester.cpp @@ -224,7 +224,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); } - err = gExchangeManager.Init(&gSessionManager); + err = gExchangeManager.Init(chip::kTestControllerNodeId, &gUDPManager, &gSessionManager); SuccessOrExit(err); // Start the CHIP connection to the CHIP echo responder. diff --git a/src/messaging/tests/echo/echo_responder.cpp b/src/messaging/tests/echo/echo_responder.cpp index 2a44aa36991b7c..fee1a69a97f340 100644 --- a/src/messaging/tests/echo/echo_responder.cpp +++ b/src/messaging/tests/echo/echo_responder.cpp @@ -102,7 +102,7 @@ int main(int argc, char * argv[]) SuccessOrExit(err); } - err = gExchangeManager.Init(&gSessionManager); + err = gExchangeManager.Init(chip::kTestDeviceNodeId, &gUDPManager, &gSessionManager); SuccessOrExit(err); err = gEchoServer.Init(&gExchangeManager); diff --git a/src/transport/CASESession.cpp b/src/transport/CASESession.cpp index aeb3e4cc2b7040..8ab738f5e6d787 100644 --- a/src/transport/CASESession.cpp +++ b/src/transport/CASESession.cpp @@ -90,6 +90,7 @@ CHIP_ERROR CASESession::EstablishSession(const Transport::PeerAddress peerAddres { CHIP_ERROR err = CHIP_NO_ERROR; + mDelegate = delegate; mConnectionState.SetPeerAddress(peerAddress); mConnectionState.SetPeerNodeId(peerNodeId);