blob: 91220a995bbd81cd5fae0d2d9863ea7be4b14f7c [file] [log] [blame]
/*
* Copyright (c) 2020-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 the CHIP reliable message protocol.
*
*/
#include <inttypes.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <messaging/ReliableMessageContext.h>
#include <core/CHIPEncoding.h>
#include <messaging/ErrorCategory.h>
#include <messaging/Flags.h>
#include <messaging/ReliableMessageMgr.h>
#include <protocols/Protocols.h>
#include <protocols/secure_channel/Constants.h>
#include <support/CodeUtils.h>
namespace chip {
namespace Messaging {
ReliableMessageContext::ReliableMessageContext() :
mConfig(gDefaultReliableMessageProtocolConfig), mNextAckTimeTick(0), mPendingPeerAckId(0)
{}
void ReliableMessageContext::RetainContext()
{
GetExchangeContext()->Retain();
}
void ReliableMessageContext::ReleaseContext()
{
GetExchangeContext()->Release();
}
bool ReliableMessageContext::AutoRequestAck() const
{
return mFlags.Has(Flags::kFlagAutoRequestAck);
}
bool ReliableMessageContext::IsAckPending() const
{
return mFlags.Has(Flags::kFlagAckPending);
}
bool ReliableMessageContext::HasPeerRequestedAck() const
{
return mFlags.Has(Flags::kFlagPeerRequestedAck);
}
bool ReliableMessageContext::HasRcvdMsgFromPeer() const
{
return mFlags.Has(Flags::kFlagMsgRcvdFromPeer);
}
void ReliableMessageContext::SetAutoRequestAck(bool autoReqAck)
{
mFlags.Set(Flags::kFlagAutoRequestAck, autoReqAck);
}
void ReliableMessageContext::SetMsgRcvdFromPeer(bool inMsgRcvdFromPeer)
{
mFlags.Set(Flags::kFlagMsgRcvdFromPeer, inMsgRcvdFromPeer);
}
void ReliableMessageContext::SetAckPending(bool inAckPending)
{
mFlags.Set(Flags::kFlagAckPending, inAckPending);
}
void ReliableMessageContext::SetPeerRequestedAck(bool inPeerRequestedAck)
{
mFlags.Set(Flags::kFlagPeerRequestedAck, inPeerRequestedAck);
}
void ReliableMessageContext::SetDropAckDebug(bool inDropAckDebug)
{
mFlags.Set(Flags::kFlagDropAckDebug, inDropAckDebug);
}
bool ReliableMessageContext::IsOccupied() const
{
return mFlags.Has(Flags::kFlagOccupied);
}
void ReliableMessageContext::SetOccupied(bool inOccupied)
{
mFlags.Set(Flags::kFlagOccupied, inOccupied);
}
bool ReliableMessageContext::ShouldDropAckDebug() const
{
return mFlags.Has(Flags::kFlagDropAckDebug);
}
ExchangeContext * ReliableMessageContext::GetExchangeContext()
{
return static_cast<ExchangeContext *>(this);
}
ReliableMessageMgr * ReliableMessageContext::GetReliableMessageMgr()
{
return static_cast<ExchangeContext *>(this)->GetExchangeMgr()->GetReliableMessageMgr();
}
CHIP_ERROR ReliableMessageContext::FlushAcks()
{
CHIP_ERROR err = CHIP_NO_ERROR;
if (IsAckPending())
{
// Send the acknowledgment as a SecureChannel::StandStandaloneAck message
err = SendStandaloneAckMessage();
if (err == CHIP_NO_ERROR)
{
#if !defined(NDEBUG)
ChipLogDetail(ExchangeManager, "Flushed pending ack for MsgId:%08" PRIX32, mPendingPeerAckId);
#endif
}
}
return err;
}
uint64_t ReliableMessageContext::GetInitialRetransmitTimeoutTick()
{
return mConfig.mInitialRetransTimeoutTick;
}
uint64_t ReliableMessageContext::GetActiveRetransmitTimeoutTick()
{
return mConfig.mActiveRetransTimeoutTick;
}
/**
* Process received Ack. Remove the corresponding message context from the RetransTable and execute the application
* callback
*
* @note
* This message is part of the CHIP Reliable Messaging protocol.
*
* @param[in] AckMsgId The msgId of incoming Ack message.
*
* @retval #CHIP_ERROR_INVALID_ACK_ID if the msgId of received Ack is not in the RetransTable.
* @retval #CHIP_NO_ERROR if the context was removed.
*
*/
CHIP_ERROR ReliableMessageContext::HandleRcvdAck(uint32_t AckMsgId)
{
CHIP_ERROR err = CHIP_NO_ERROR;
// Msg is an Ack; Check Retrans Table and remove message context
if (!GetReliableMessageMgr()->CheckAndRemRetransTable(this, AckMsgId))
{
#if !defined(NDEBUG)
ChipLogError(ExchangeManager, "CHIP MsgId:%08" PRIX32 " not in RetransTable", AckMsgId);
#endif
err = CHIP_ERROR_INVALID_ACK_ID;
// Optionally call an application callback with this error.
}
else
{
#if !defined(NDEBUG)
ChipLogDetail(ExchangeManager, "Removed CHIP MsgId:%08" PRIX32 " from RetransTable", AckMsgId);
#endif
}
return err;
}
CHIP_ERROR ReliableMessageContext::HandleNeedsAck(uint32_t MessageId, BitFlags<MessageFlagValues> MsgFlags)
{
CHIP_ERROR err = CHIP_NO_ERROR;
// Skip processing ack if drop ack debug is enabled.
if (ShouldDropAckDebug())
return err;
// Expire any virtual ticks that have expired so all wakeup sources reflect the current time
GetReliableMessageMgr()->ExpireTicks();
// If the message IS a duplicate.
if (MsgFlags.Has(MessageFlagValues::kDuplicateMessage))
{
#if !defined(NDEBUG)
ChipLogDetail(ExchangeManager, "Forcing tx of solitary ack for duplicate MsgId:%08" PRIX32, MessageId);
#endif
// Is there pending ack for a different message id.
bool wasAckPending = IsAckPending() && mPendingPeerAckId != MessageId;
// Temporary store currently pending ack id (even if there is none).
uint32_t tempAckId = mPendingPeerAckId;
// Set the pending ack id.
mPendingPeerAckId = MessageId;
// Send the Ack for the duplication message in a SecureChannel::StandaloneAck message.
err = SendStandaloneAckMessage();
// If there was pending ack for a different message id.
if (wasAckPending)
{
// Restore previously pending ack id.
mPendingPeerAckId = tempAckId;
SetAckPending(true);
}
SuccessOrExit(err);
}
// Otherwise, the message IS NOT a duplicate.
else
{
if (IsAckPending())
{
#if !defined(NDEBUG)
ChipLogDetail(ExchangeManager, "Pending ack queue full; forcing tx of solitary ack for MsgId:%08" PRIX32,
mPendingPeerAckId);
#endif
// Send the Ack for the currently pending Ack in a SecureChannel::StandaloneAck message.
err = SendStandaloneAckMessage();
SuccessOrExit(err);
}
// Replace the Pending ack id.
mPendingPeerAckId = MessageId;
mNextAckTimeTick =
static_cast<uint16_t>(CHIP_CONFIG_RMP_DEFAULT_ACK_TIMEOUT_TICK +
GetReliableMessageMgr()->GetTickCounterFromTimeDelta(System::Timer::GetCurrentEpoch()));
SetAckPending(true);
}
exit:
// Schedule next physical wakeup
GetReliableMessageMgr()->StartTimer();
return err;
}
CHIP_ERROR ReliableMessageContext::SendStandaloneAckMessage()
{
CHIP_ERROR err = CHIP_NO_ERROR;
// Allocate a buffer for the null message
System::PacketBufferHandle msgBuf = MessagePacketBuffer::New(0);
VerifyOrExit(!msgBuf.IsNull(), err = CHIP_ERROR_NO_MEMORY);
// Send the null message
#if !defined(NDEBUG)
ChipLogDetail(ExchangeManager, "Sending Standalone Ack for MsgId:%08" PRIX32, mPendingPeerAckId);
#endif
err = GetExchangeContext()->SendMessage(Protocols::SecureChannel::MsgType::StandaloneAck, std::move(msgBuf),
BitFlags<SendMessageFlags>{ SendMessageFlags::kNoAutoRequestAck });
exit:
if (IsSendErrorNonCritical(err))
{
ChipLogError(ExchangeManager, "Non-crit err %ld sending solitary ack", long(err));
err = CHIP_NO_ERROR;
}
if (err != CHIP_NO_ERROR)
{
ChipLogError(ExchangeManager, "Failed to send Solitary ack for MsgId:%08" PRIX32 ":%ld", mPendingPeerAckId, (long) err);
}
return err;
}
} // namespace Messaging
} // namespace chip