blob: 932142bc4755cbdcf681691e41a8d7ccc636c03e [file] [log] [blame]
/*
*
* Copyright (c) 2020-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 the ExchangeManager class.
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <cstring>
#include <inttypes.h>
#include <stddef.h>
#include <crypto/RandUtils.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/support/CHIPFaultInjection.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <protocols/Protocols.h>
using namespace chip::Encoding;
using namespace chip::Inet;
using namespace chip::System;
namespace chip {
namespace Messaging {
/**
* Constructor for the ExchangeManager class.
* It sets the state to kState_NotInitialized.
*
* @note
* The class must be initialized via ExchangeManager::Init()
* prior to use.
*
*/
ExchangeManager::ExchangeManager() : mReliableMessageMgr(mContextPool)
{
mState = State::kState_NotInitialized;
}
CHIP_ERROR ExchangeManager::Init(SessionManager * sessionManager)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrReturnError(mState == State::kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE);
mSessionManager = sessionManager;
mNextExchangeId = chip::Crypto::GetRandU16();
mNextKeyId = 0;
for (auto & handler : UMHandlerPool)
{
// Mark all handlers as unallocated. This handles both initial
// initialization and the case when the consumer shuts us down and
// then re-initializes without removing registered handlers.
handler.Reset();
}
sessionManager->RegisterReleaseDelegate(*this);
sessionManager->SetMessageDelegate(this);
mReliableMessageMgr.Init(sessionManager->SystemLayer());
ReturnErrorOnFailure(mDefaultExchangeDispatch.Init(mSessionManager));
mState = State::kState_Initialized;
return err;
}
CHIP_ERROR ExchangeManager::Shutdown()
{
mReliableMessageMgr.Shutdown();
mContextPool.ForEachActiveObject([](auto * ec) {
// There should be no active object in the pool
VerifyOrDie(false);
return Loop::Continue;
});
if (mSessionManager != nullptr)
{
mSessionManager->SetMessageDelegate(nullptr);
mSessionManager->UnregisterReleaseDelegate(*this);
mSessionManager = nullptr;
}
mState = State::kState_NotInitialized;
return CHIP_NO_ERROR;
}
ExchangeContext * ExchangeManager::NewContext(SessionHandle session, ExchangeDelegate * delegate)
{
return mContextPool.CreateObject(this, mNextExchangeId++, session, true, delegate);
}
CHIP_ERROR ExchangeManager::RegisterUnsolicitedMessageHandlerForProtocol(Protocols::Id protocolId, ExchangeDelegate * delegate)
{
return RegisterUMH(protocolId, kAnyMessageType, delegate);
}
CHIP_ERROR ExchangeManager::RegisterUnsolicitedMessageHandlerForType(Protocols::Id protocolId, uint8_t msgType,
ExchangeDelegate * delegate)
{
return RegisterUMH(protocolId, static_cast<int16_t>(msgType), delegate);
}
CHIP_ERROR ExchangeManager::UnregisterUnsolicitedMessageHandlerForProtocol(Protocols::Id protocolId)
{
return UnregisterUMH(protocolId, kAnyMessageType);
}
CHIP_ERROR ExchangeManager::UnregisterUnsolicitedMessageHandlerForType(Protocols::Id protocolId, uint8_t msgType)
{
return UnregisterUMH(protocolId, static_cast<int16_t>(msgType));
}
CHIP_ERROR ExchangeManager::RegisterUMH(Protocols::Id protocolId, int16_t msgType, ExchangeDelegate * delegate)
{
UnsolicitedMessageHandler * selected = nullptr;
for (auto & umh : UMHandlerPool)
{
if (!umh.IsInUse())
{
if (selected == nullptr)
selected = &umh;
}
else if (umh.Matches(protocolId, msgType))
{
umh.Delegate = delegate;
return CHIP_NO_ERROR;
}
}
if (selected == nullptr)
return CHIP_ERROR_TOO_MANY_UNSOLICITED_MESSAGE_HANDLERS;
selected->Delegate = delegate;
selected->ProtocolId = protocolId;
selected->MessageType = msgType;
SYSTEM_STATS_INCREMENT(chip::System::Stats::kExchangeMgr_NumUMHandlers);
return CHIP_NO_ERROR;
}
CHIP_ERROR ExchangeManager::UnregisterUMH(Protocols::Id protocolId, int16_t msgType)
{
for (auto & umh : UMHandlerPool)
{
if (umh.IsInUse() && umh.Matches(protocolId, msgType))
{
umh.Reset();
SYSTEM_STATS_DECREMENT(chip::System::Stats::kExchangeMgr_NumUMHandlers);
return CHIP_NO_ERROR;
}
}
return CHIP_ERROR_NO_UNSOLICITED_MESSAGE_HANDLER;
}
void ExchangeManager::OnMessageReceived(const PacketHeader & packetHeader, const PayloadHeader & payloadHeader,
SessionHandle session, const Transport::PeerAddress & source, DuplicateMessage isDuplicate,
System::PacketBufferHandle && msgBuf)
{
UnsolicitedMessageHandler * matchingUMH = nullptr;
ChipLogProgress(ExchangeManager,
"Received message of type " ChipLogFormatMessageType " with protocolId " ChipLogFormatProtocolId
" and MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchangeId,
payloadHeader.GetMessageType(), ChipLogValueProtocolId(payloadHeader.GetProtocolID()),
packetHeader.GetMessageCounter(), ChipLogValueExchangeIdFromReceivedHeader(payloadHeader));
MessageFlags msgFlags;
if (isDuplicate == DuplicateMessage::Yes)
{
msgFlags.Set(MessageFlagValues::kDuplicateMessage);
}
// Skip retrieval of exchange for group message since no exchange is stored
// for group msg (optimization)
if (!packetHeader.IsGroupSession())
{
// Search for an existing exchange that the message applies to. If a match is found...
bool found = false;
mContextPool.ForEachActiveObject([&](auto * ec) {
if (ec->MatchExchange(session, packetHeader, payloadHeader))
{
// Found a matching exchange. Set flag for correct subsequent MRP
// retransmission timeout selection.
if (!ec->HasRcvdMsgFromPeer())
{
ec->SetMsgRcvdFromPeer(true);
}
ChipLogDetail(ExchangeManager, "Found matching exchange: " ChipLogFormatExchange ", Delegate: %p",
ChipLogValueExchange(ec), ec->GetDelegate());
// Matched ExchangeContext; send to message handler.
ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, source, msgFlags, std::move(msgBuf));
found = true;
return Loop::Break;
}
return Loop::Continue;
});
if (found)
{
return;
}
}
// If it's not a duplicate message, search for an unsolicited message handler if it is marked as being sent by an initiator.
// Since we didn't find an existing exchange that matches the message, it must be an unsolicited message. However all
// unsolicited messages must be marked as being from an initiator.
if (!msgFlags.Has(MessageFlagValues::kDuplicateMessage) && payloadHeader.IsInitiator())
{
// Search for an unsolicited message handler that can handle the message. Prefer handlers that can explicitly
// handle the message type over handlers that handle all messages for a profile.
matchingUMH = nullptr;
for (auto & umh : UMHandlerPool)
{
if (umh.IsInUse() && payloadHeader.HasProtocol(umh.ProtocolId))
{
if (umh.MessageType == payloadHeader.GetMessageType())
{
matchingUMH = &umh;
break;
}
if (umh.MessageType == kAnyMessageType)
matchingUMH = &umh;
}
}
}
// Discard the message if it isn't marked as being sent by an initiator and the message does not need to send
// an ack to the peer.
else if (!payloadHeader.NeedsAck())
{
// Using same error message for all errors to reduce code size.
ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %s", ErrorStr(CHIP_ERROR_UNSOLICITED_MSG_NO_ORIGINATOR));
return;
}
// If we found a handler or we need to send an ack, create an exchange to
// handle the message.
if (matchingUMH != nullptr || payloadHeader.NeedsAck())
{
ExchangeDelegate * delegate = matchingUMH ? matchingUMH->Delegate : nullptr;
// If rcvd msg is from initiator then this exchange is created as not Initiator.
// If rcvd msg is not from initiator then this exchange is created as Initiator.
// Note that if matchingUMH is not null then rcvd msg if from initiator.
// TODO: Figure out which channel to use for the received message
ExchangeContext * ec =
mContextPool.CreateObject(this, payloadHeader.GetExchangeID(), session, !payloadHeader.IsInitiator(), delegate);
if (ec == nullptr)
{
// Using same error message for all errors to reduce code size.
ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %s", ErrorStr(CHIP_ERROR_NO_MEMORY));
return;
}
ChipLogDetail(ExchangeManager, "Handling via exchange: " ChipLogFormatExchange ", Delegate: %p", ChipLogValueExchange(ec),
ec->GetDelegate());
if (ec->IsEncryptionRequired() != packetHeader.IsEncrypted())
{
ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %s", ErrorStr(CHIP_ERROR_INVALID_MESSAGE_TYPE));
ec->Close();
return;
}
CHIP_ERROR err = ec->HandleMessage(packetHeader.GetMessageCounter(), payloadHeader, source, msgFlags, std::move(msgBuf));
if (err != CHIP_NO_ERROR)
{
// Using same error message for all errors to reduce code size.
ChipLogError(ExchangeManager, "OnMessageReceived failed, err = %s", ErrorStr(err));
}
}
}
void ExchangeManager::OnSessionReleased(SessionHandle session)
{
ExpireExchangesForSession(session);
}
void ExchangeManager::ExpireExchangesForSession(SessionHandle session)
{
mContextPool.ForEachActiveObject([&](auto * ec) {
if (ec->mSession.HasValue() && ec->mSession.Value() == session)
{
ec->OnConnectionExpired();
// Continue to iterate because there can be multiple exchanges
// associated with the connection.
}
return Loop::Continue;
});
}
void ExchangeManager::CloseAllContextsForDelegate(const ExchangeDelegate * delegate)
{
mContextPool.ForEachActiveObject([&](auto * ec) {
if (ec->GetDelegate() == delegate)
{
// Make sure to null out the delegate before closing the context, so
// we don't notify the delegate that the context is closing. We
// have to do this, because the delegate might be partially
// destroyed by this point.
ec->SetDelegate(nullptr);
ec->Close();
}
return Loop::Continue;
});
}
} // namespace Messaging
} // namespace chip