/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include using namespace chip::DeviceLayer; namespace chip { namespace Messaging { ReliableMessageContext::ReliableMessageContext() : mNextAckTime(0), mPendingPeerAckMessageCounter(0) {} ExchangeContext * ReliableMessageContext::GetExchangeContext() { return static_cast(this); } ReliableMessageMgr * ReliableMessageContext::GetReliableMessageMgr() { return static_cast(this)->GetExchangeMgr()->GetReliableMessageMgr(); } void ReliableMessageContext::SetWaitingForAck(bool waitingForAck) { mFlags.Set(Flags::kFlagWaitingForAck, waitingForAck); } CHIP_ERROR ReliableMessageContext::FlushAcks() { CHIP_ERROR err = CHIP_NO_ERROR; if (IsAckPending()) { err = SendStandaloneAckMessage(); if (err == CHIP_NO_ERROR) { ChipLogDetail(ExchangeManager, "Flushed pending ack for MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchange, mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext())); } } return err; } /** * 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] ackMessageCounter The acknowledged message counter of the incoming message. */ void ReliableMessageContext::HandleRcvdAck(uint32_t ackMessageCounter) { // Msg is an Ack; Check Retrans Table and remove message context if (GetReliableMessageMgr()->CheckAndRemRetransTable(this, ackMessageCounter)) { SetWaitingForResponseOrAck(false); } else { // This can happen quite easily due to a packet with a piggyback ack // being lost and retransmitted. ChipLogDetail(ExchangeManager, "CHIP MessageCounter:" ChipLogFormatMessageCounter " not in RetransTable on exchange " ChipLogFormatExchange, ackMessageCounter, ChipLogValueExchange(GetExchangeContext())); } } CHIP_ERROR ReliableMessageContext::HandleNeedsAck(uint32_t messageCounter, BitFlags messageFlags) { CHIP_ERROR err = HandleNeedsAckInner(messageCounter, messageFlags); // Schedule next physical wakeup on function exit GetReliableMessageMgr()->StartTimer(); return err; } CHIP_ERROR ReliableMessageContext::HandleNeedsAckInner(uint32_t messageCounter, BitFlags messageFlags) { // If the message IS a duplicate there will never be a response to it, so we // should not wait for one and just immediately send a standalone ack. if (messageFlags.Has(MessageFlagValues::kDuplicateMessage)) { ChipLogDetail(ExchangeManager, "Forcing tx of solitary ack for duplicate MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchange, messageCounter, ChipLogValueExchange(GetExchangeContext())); bool wasAckPending = IsAckPending() && mPendingPeerAckMessageCounter != messageCounter; bool messageCounterWasValid = HasPiggybackAckPending(); // Temporary store currently pending ack message counter (even if there is none). uint32_t tempAckMessageCounter = mPendingPeerAckMessageCounter; SetPendingPeerAckMessageCounter(messageCounter); CHIP_ERROR err = SendStandaloneAckMessage(); if (wasAckPending) { // Restore previously pending ack message counter. SetPendingPeerAckMessageCounter(tempAckMessageCounter); } else if (messageCounterWasValid) { // Restore the previous value, so later piggybacks will pick it up, // but don't set out "ack is pending" state, because we didn't use // to have it set. mPendingPeerAckMessageCounter = tempAckMessageCounter; } // Otherwise don't restore the invalid old mPendingPeerAckMessageCounter // value, so we preserve the invariant that once we have had an ack // pending we always have a valid mPendingPeerAckMessageCounter. return err; } // Otherwise, the message IS NOT a duplicate. if (IsAckPending()) { ChipLogDetail(ExchangeManager, "Pending ack queue full; forcing tx of solitary ack for MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchange, mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext())); // Send the Ack for the currently pending Ack in a SecureChannel::StandaloneAck message. ReturnErrorOnFailure(SendStandaloneAckMessage()); } // Replace the Pending ack message counter. SetPendingPeerAckMessageCounter(messageCounter); using namespace System::Clock::Literals; mNextAckTime = System::SystemClock().GetMonotonicTimestamp() + CHIP_CONFIG_RMP_DEFAULT_ACK_TIMEOUT; return CHIP_NO_ERROR; } CHIP_ERROR ReliableMessageContext::SendStandaloneAckMessage() { // Allocate a buffer for the null message System::PacketBufferHandle msgBuf = MessagePacketBuffer::New(0); if (msgBuf.IsNull()) { return CHIP_ERROR_NO_MEMORY; } CHIP_ERROR err = GetExchangeContext()->SendMessage(Protocols::SecureChannel::MsgType::StandaloneAck, std::move(msgBuf), BitFlags{ SendMessageFlags::kNoAutoRequestAck }); if (IsSendErrorNonCritical(err)) { ChipLogError(ExchangeManager, "Non-crit err %" CHIP_ERROR_FORMAT " sending solitary ack for MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchange, err.Format(), mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext())); return CHIP_NO_ERROR; } if (err != CHIP_NO_ERROR) { ChipLogError(ExchangeManager, "Failed to send Solitary ack for MessageCounter:" ChipLogFormatMessageCounter " on exchange " ChipLogFormatExchange ":%" CHIP_ERROR_FORMAT, mPendingPeerAckMessageCounter, ChipLogValueExchange(GetExchangeContext()), err.Format()); } return err; } void ReliableMessageContext::SetPendingPeerAckMessageCounter(uint32_t aPeerAckMessageCounter) { mPendingPeerAckMessageCounter = aPeerAckMessageCounter; SetAckPending(true); mFlags.Set(Flags::kFlagAckMessageCounterIsValid); } } // namespace Messaging } // namespace chip