diff --git a/src/app/clusters/ota-requestor/DefaultOTARequestor.h b/src/app/clusters/ota-requestor/DefaultOTARequestor.h index e0e45c2709af2e..cdc34564414be7 100644 --- a/src/app/clusters/ota-requestor/DefaultOTARequestor.h +++ b/src/app/clusters/ota-requestor/DefaultOTARequestor.h @@ -165,6 +165,7 @@ class DefaultOTARequestor : public OTARequestorInterface, public BDXDownloader:: if (!payloadHeader.HasMessageType(chip::Protocols::SecureChannel::MsgType::StatusReport)) { ec->WillSendMessage(); + ec->SetShouldPeerBeActive(true); } return CHIP_NO_ERROR; diff --git a/src/messaging/ExchangeContext.cpp b/src/messaging/ExchangeContext.cpp index 8c085025456ba8..e4ada0d6ca95a6 100644 --- a/src/messaging/ExchangeContext.cpp +++ b/src/messaging/ExchangeContext.cpp @@ -537,6 +537,10 @@ CHIP_ERROR ExchangeContext::HandleMessage(uint32_t messageCounter, const Payload // layer has completed its work on the ExchangeContext. ExchangeHandle ref(*this); + // If we received a message from the Peer and we are still expecting a response, we know the peer is active + // If we receive a message from the Peer but we are not expecting a response, we don't know if the peer is still active + SetShouldPeerBeActive(IsResponseExpected()); + bool isStandaloneAck = payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::StandaloneAck); bool isDuplicate = msgFlags.Has(MessageFlagValues::kDuplicateMessage); @@ -565,7 +569,6 @@ CHIP_ERROR ExchangeContext::HandleMessage(uint32_t messageCounter, const Payload if (payloadHeader.NeedsAck()) { // An acknowledgment needs to be sent back to the peer for this message on this exchange, - HandleNeedsAck(messageCounter, msgFlags); } } @@ -628,6 +631,9 @@ CHIP_ERROR ExchangeContext::HandleMessage(uint32_t messageCounter, const Payload // If the context was expecting a response to a previously sent message, this message // is implicitly that response. SetResponseExpected(false); + + // If we received the expected response, we don't if the peer is still active after having sent the response + SetShouldPeerBeActive(false); } // Don't send messages on to our delegate if our dispatch does not allow @@ -689,5 +695,21 @@ void ExchangeContext::ExchangeSessionHolder::GrabExpiredSession(const SessionHan GrabUnchecked(session); } +bool ExchangeContext::ShouldPeerBeActive() +{ + // If we are not the initiator, it means the peer sent the first message. + // This means our peer is active if we are sending a message to them. + if (!IsInitiator()) + return true; + + // If we create an Ephemeral Exchange, it was just to generate a StandaloneAck + // Since we are sending an ack, we know the peer is active + if (IsEphemeralExchange()) + return true; + + // Return previously stored state if we have no absolute answer + return mFlags.Has(Flags::kPeerShouldBeActive); +} + } // namespace Messaging } // namespace chip diff --git a/src/messaging/ExchangeContext.h b/src/messaging/ExchangeContext.h index e30cf0593b3ed9..246d2c182283e5 100644 --- a/src/messaging/ExchangeContext.h +++ b/src/messaging/ExchangeContext.h @@ -214,6 +214,47 @@ class DLL_EXPORT ExchangeContext : public ReliableMessageContext, */ bool IsSendExpected() const { return mFlags.Has(Flags::kFlagWillSendMessage); } + /** + * Notifies the exchange that our consummer is going to send a message where it knows the peer (receiver) should be active + * + * This API is used to set the ShouldPeerBeActive flag. + * The ShouldPeerBeActive flag allows the consummer of the exchange to notify the exchange that the peer (receiver) + * will be active for the next message the consummer will send. + * This allows the consummer to notify the exchange on the state of the peer with the context of the messages being sent. + * + * The ShouldPeerBeActive flag is only used when calculating the next retransmission time in the ReliableMessageMgr. + * If the ShouldPeerBeActive flag is true, we will used the Active Retransmission timeout when calculating the next + * retransmission time. If the ShouldPeerBeActive flag is false, we will rely on other information to determine which + * retransmission timeout should be used. See the ShouldPeerBeActive API for the alternative means to determine if the Peer + * (receiver) is active. + * + * The first message of the exchange set as the initiator should never have the ShouldPeerBeActive flag to true. + * The API should only called after receiving a message from the peer and after calling the WillSendMessage API. + * The next message sent will then use the active retransmission time out. + * The flag is set to false each time we receive the response from our peer (receiver). + * As such, the API needs to be called each time we send another message over the exchange. + * + * The API call is not mandatory for the communication to be successful. + * If the call is not done, we will rely on information within the exchange context. + * + * @param shouldPeerBeActive true if the peer should be active, false if we don't know or it should not be + */ + void SetShouldPeerBeActive(bool shouldPeerBeActive) { mFlags.Set(Flags::kPeerShouldBeActive, shouldPeerBeActive); } + + /** + * Function checks with information available in the ExchangeContext if the peer should be active or not + * API should only be used when we are determining if we need to use the Active or Idle retransmission timeout + * + * If we are not initiator -> peer is active + * If ephemeral context -> peer is active + * else we default to information provided by the consummer + * + * @return true Based on available information, Peer should be active + * @return false We weren't able to determine if the peer is active. + * We don't know in which state the peer is. + */ + bool ShouldPeerBeActive(); + #if CONFIG_BUILD_FOR_HOST_UNIT_TEST SessionHolder & GetSessionHolder() { return mSession; } @@ -292,25 +333,6 @@ class DLL_EXPORT ExchangeContext : public ReliableMessageContext, */ void MessageHandled(); - /** - * Updates Sleepy End Device intervals mode in the following way: - * - does nothing for exchanges over Bluetooth LE - * - requests active mode if there are more messages, - * including MRP acknowledgements, expected to be sent or received on - * this exchange. - * - withdraws the request for active mode, otherwise. - */ - void UpdateSEDIntervalMode(); - - /** - * Requests or withdraws the request for Sleepy End Device active mode - * based on the argument value. - * - * Note that the device switches to the idle mode if no - * exchange nor other component requests the active mode. - */ - void UpdateSEDIntervalMode(bool activeMode); - static ExchangeMessageDispatch & GetMessageDispatch(bool isEphemeralExchange, ExchangeDelegate * delegate); // If SetAutoReleaseSession() is called, this exchange must be using a SecureSession, and should diff --git a/src/messaging/ReliableMessageContext.cpp b/src/messaging/ReliableMessageContext.cpp index ab717e34259111..39a142d52e489c 100644 --- a/src/messaging/ReliableMessageContext.cpp +++ b/src/messaging/ReliableMessageContext.cpp @@ -114,7 +114,6 @@ void ReliableMessageContext::HandleRcvdAck(uint32_t ackMessageCounter) } CHIP_ERROR ReliableMessageContext::HandleNeedsAck(uint32_t messageCounter, BitFlags messageFlags) - { CHIP_ERROR err = HandleNeedsAckInner(messageCounter, messageFlags); diff --git a/src/messaging/ReliableMessageContext.h b/src/messaging/ReliableMessageContext.h index 2ee9fe50a2f763..00262a766f9067 100644 --- a/src/messaging/ReliableMessageContext.h +++ b/src/messaging/ReliableMessageContext.h @@ -123,9 +123,6 @@ class ReliableMessageContext /// Set if this exchange is requesting Sleepy End Device active mode void SetRequestingActiveMode(bool activeMode); - /// Determine whether this exchange is requesting Sleepy End Device active mode - bool IsRequestingActiveMode() const; - /// Determine whether this exchange is a EphemeralExchange for replying a StandaloneAck bool IsEphemeralExchange() const; @@ -164,18 +161,33 @@ class ReliableMessageContext /// When set, we have had Close() or Abort() called on us already. kFlagClosed = (1u << 7), - /// When set, signifies that the exchange is requesting Sleepy End Device active mode. - kFlagActiveMode = (1u << 8), - /// When set, signifies that the exchange created sorely for replying a StandaloneAck - kFlagEphemeralExchange = (1u << 9), + kFlagEphemeralExchange = (1u << 8), /// When set, ignore session being released, because we are releasing it ourselves. - kFlagIgnoreSessionRelease = (1u << 10), + kFlagIgnoreSessionRelease = (1u << 9), + + // When set, sender knows that the peer (receiver) should currently be active + // When bit is not set, we don't know if the peer (receiver) is active or not + kPeerShouldBeActive = (1u << 10) }; BitFlags mFlags; // Internal state flags + /** + * Function checks with information available in the ExchangeContext if the peer should be active or not + * API should only be used when we are determining if we need to use the Active or Idle retransmission timeout + * + * If we are not initiator -> peer is active + * If ephemeral context -> peer is active + * else we default to information provided by the consummer + * + * @return true Based on available information, Peer should be active + * @return false We weren't able to determine if the peer is active. + * We don't know in which state the peer is. + */ + bool ShouldPeerBeActive(); + private: void HandleRcvdAck(uint32_t ackMessageCounter); CHIP_ERROR HandleNeedsAck(uint32_t messageCounter, BitFlags messageFlags); @@ -226,11 +238,6 @@ inline bool ReliableMessageContext::HasPiggybackAckPending() const return mFlags.Has(Flags::kFlagAckMessageCounterIsValid); } -inline bool ReliableMessageContext::IsRequestingActiveMode() const -{ - return mFlags.Has(Flags::kFlagActiveMode); -} - inline void ReliableMessageContext::SetAutoRequestAck(bool autoReqAck) { mFlags.Set(Flags::kFlagAutoRequestAck, autoReqAck); @@ -241,11 +248,6 @@ inline void ReliableMessageContext::SetAckPending(bool inAckPending) mFlags.Set(Flags::kFlagAckPending, inAckPending); } -inline void ReliableMessageContext::SetRequestingActiveMode(bool activeMode) -{ - mFlags.Set(Flags::kFlagActiveMode, activeMode); -} - inline bool ReliableMessageContext::IsEphemeralExchange() const { return mFlags.Has(Flags::kFlagEphemeralExchange); diff --git a/src/messaging/ReliableMessageMgr.cpp b/src/messaging/ReliableMessageMgr.cpp index 68818585df3075..54047eb5a968a1 100644 --- a/src/messaging/ReliableMessageMgr.cpp +++ b/src/messaging/ReliableMessageMgr.cpp @@ -173,10 +173,7 @@ void ReliableMessageMgr::ExecuteActions() " Send Cnt %d", messageCounter, ChipLogValueExchange(&entry->ec.Get()), entry->sendCount); - // Choose active/idle timeout from PeerActiveMode of session per 4.11.2.1. Retransmissions. - System::Clock::Timestamp baseTimeout = entry->ec->GetSessionHandle()->GetMRPBaseTimeout(); - System::Clock::Timestamp backoff = ReliableMessageMgr::GetBackoff(baseTimeout, entry->sendCount); - entry->nextRetransTime = System::SystemClock().GetMonotonicTimestamp() + backoff; + CalculateNextRetransTime(entry); SendFromRetransTable(entry); return Loop::Continue; @@ -277,10 +274,7 @@ System::Clock::Timestamp ReliableMessageMgr::GetBackoff(System::Clock::Timestamp void ReliableMessageMgr::StartRetransmision(RetransTableEntry * entry) { - // Choose active/idle timeout from PeerActiveMode of session per 4.11.2.1. Retransmissions. - System::Clock::Timestamp baseTimeout = entry->ec->GetSessionHandle()->GetMRPBaseTimeout(); - System::Clock::Timestamp backoff = ReliableMessageMgr::GetBackoff(baseTimeout, entry->sendCount); - entry->nextRetransTime = System::SystemClock().GetMonotonicTimestamp() + backoff; + CalculateNextRetransTime(entry); StartTimer(); } @@ -471,6 +465,26 @@ CHIP_ERROR ReliableMessageMgr::MapSendError(CHIP_ERROR error, uint16_t exchangeI return error; } +void ReliableMessageMgr::CalculateNextRetransTime(RetransTableEntry * entry) +{ + System::Clock::Timestamp baseTimeout = System::Clock::Milliseconds64(0); + + if (entry->ec->ShouldPeerBeActive()) + { + // If we know the peer is active with the exchangeContext, use the Active Retrans timeout + baseTimeout = entry->ec->GetSessionHandle()->GetRemoteMRPConfig().mActiveRetransTimeout; + } + else + { + // If the exchange context doesn't know if the peer is active or not. + // Choose active/idle timeout from PeerActiveMode of session per 4.11.2.1. Retransmissions. + baseTimeout = entry->ec->GetSessionHandle()->GetMRPBaseTimeout(); + } + + System::Clock::Timestamp backoff = ReliableMessageMgr::GetBackoff(baseTimeout, entry->sendCount); + entry->nextRetransTime = System::SystemClock().GetMonotonicTimestamp() + backoff; +} + #if CHIP_CONFIG_TEST int ReliableMessageMgr::TestGetCountRetransTable() { diff --git a/src/messaging/ReliableMessageMgr.h b/src/messaging/ReliableMessageMgr.h index ea31e46f4df066..953b46dde9ef74 100644 --- a/src/messaging/ReliableMessageMgr.h +++ b/src/messaging/ReliableMessageMgr.h @@ -206,6 +206,14 @@ class ReliableMessageMgr #endif // CHIP_CONFIG_TEST private: + /** + * Calculates the next retransmission time for the entry + * Function sets the nextRetransTime of the entry + * + * @param[in/out] entry RetransTableEntry for which we need to calculate the nextRetransTime + */ + void CalculateNextRetransTime(RetransTableEntry * entry); + ObjectPool & mContextPool; chip::System::Layer * mSystemLayer; diff --git a/src/messaging/tests/TestReliableMessageProtocol.cpp b/src/messaging/tests/TestReliableMessageProtocol.cpp index 7596071f07af8a..db5d9ce5187e10 100644 --- a/src/messaging/tests/TestReliableMessageProtocol.cpp +++ b/src/messaging/tests/TestReliableMessageProtocol.cpp @@ -295,7 +295,35 @@ struct BackoffComplianceTestVector theBackoffComplianceTestVector[] = { }, }; -void CheckAddClearRetrans(nlTestSuite * inSuite, void * inContext) +} // namespace + +class TestReliableMessageProtocol +{ +public: + static void CheckAddClearRetrans(nlTestSuite * inSuite, void * inContext); + static void CheckResendApplicationMessage(nlTestSuite * inSuite, void * inContext); + static void CheckCloseExchangeAndResendApplicationMessage(nlTestSuite * inSuite, void * inContext); + static void CheckFailedMessageRetainOnSend(nlTestSuite * inSuite, void * inContext); + static void CheckResendApplicationMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext); + static void CheckResendSessionEstablishmentMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext); + static void CheckDuplicateMessage(nlTestSuite * inSuite, void * inContext); + static void CheckDuplicateMessageClosedExchange(nlTestSuite * inSuite, void * inContext); + static void CheckDuplicateOldMessageClosedExchange(nlTestSuite * inSuite, void * inContext); + static void CheckReceiveAfterStandaloneAck(nlTestSuite * inSuite, void * inContext); + static void CheckPiggybackAfterPiggyback(nlTestSuite * inSuite, void * inContext); + static void CheckSendUnsolicitedStandaloneAckMessage(nlTestSuite * inSuite, void * inContext); + static void CheckSendStandaloneAckMessage(nlTestSuite * inSuite, void * inContext); + static void CheckMessageAfterClosed(nlTestSuite * inSuite, void * inContext); + static void CheckUnencryptedMessageReceiveFailure(nlTestSuite * inSuite, void * inContext); + static void CheckLostResponseWithPiggyback(nlTestSuite * inSuite, void * inContext); + static void CheckLostStandaloneAck(nlTestSuite * inSuite, void * inContext); + static void CheckIsPeerActiveNotInitiator(nlTestSuite * inSuite, void * inContext); + static void CheckGetBackoff(nlTestSuite * inSuite, void * inContext); + static int InitializeTestCase(void * inContext); +}; + + +void TestReliableMessageProtocol::CheckAddClearRetrans(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -344,7 +372,7 @@ void CheckAddClearRetrans(nlTestSuite * inSuite, void * inContext) * - PEER to acknowledge message * - Observe DUT signal successful reliable transmission */ -void CheckResendApplicationMessage(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckResendApplicationMessage(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); BackoffComplianceTestVector * expectedBackoff; @@ -461,7 +489,7 @@ void CheckResendApplicationMessage(nlTestSuite * inSuite, void * inContext) exchange->Close(); } -void CheckCloseExchangeAndResendApplicationMessage(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckCloseExchangeAndResendApplicationMessage(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -521,7 +549,7 @@ void CheckCloseExchangeAndResendApplicationMessage(nlTestSuite * inSuite, void * NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckFailedMessageRetainOnSend(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckFailedMessageRetainOnSend(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -566,7 +594,7 @@ void CheckFailedMessageRetainOnSend(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckUnencryptedMessageReceiveFailure(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckUnencryptedMessageReceiveFailure(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -605,7 +633,7 @@ void CheckUnencryptedMessageReceiveFailure(nlTestSuite * inSuite, void * inConte NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckResendApplicationMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckResendApplicationMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -667,7 +695,7 @@ void CheckResendApplicationMessageWithPeerExchange(nlTestSuite * inSuite, void * NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); } -void CheckDuplicateMessageClosedExchange(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckDuplicateMessageClosedExchange(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -734,7 +762,7 @@ void CheckDuplicateMessageClosedExchange(nlTestSuite * inSuite, void * inContext NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckDuplicateOldMessageClosedExchange(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -831,7 +859,7 @@ void CheckDuplicateOldMessageClosedExchange(nlTestSuite * inSuite, void * inCont NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckResendSessionEstablishmentMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckResendSessionEstablishmentMessageWithPeerExchange(nlTestSuite * inSuite, void * inContext) { // Making this static to reduce stack usage, as some platforms have limits on stack size. static Test::MessagingContext ctx; @@ -899,7 +927,7 @@ void CheckResendSessionEstablishmentMessageWithPeerExchange(nlTestSuite * inSuit ctx.ShutdownAndRestoreExisting(inctx); } -void CheckDuplicateMessage(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckDuplicateMessage(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -969,7 +997,7 @@ void CheckDuplicateMessage(nlTestSuite * inSuite, void * inContext) mockReceiver.CloseExchangeIfNeeded(); } -void CheckReceiveAfterStandaloneAck(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckReceiveAfterStandaloneAck(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -1059,7 +1087,7 @@ void CheckReceiveAfterStandaloneAck(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckPiggybackAfterPiggyback(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckPiggybackAfterPiggyback(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -1190,7 +1218,7 @@ void CheckPiggybackAfterPiggyback(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckSendUnsolicitedStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckSendUnsolicitedStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) { /** * Tests sending a standalone ack message that is: @@ -1241,7 +1269,7 @@ void CheckSendUnsolicitedStandaloneAckMessage(nlTestSuite * inSuite, void * inCo NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckSendStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckSendStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) { TestContext & ctx = *reinterpret_cast(inContext); @@ -1261,7 +1289,7 @@ void CheckSendStandaloneAckMessage(nlTestSuite * inSuite, void * inContext) exchange->Close(); } -void CheckMessageAfterClosed(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckMessageAfterClosed(nlTestSuite * inSuite, void * inContext) { /** * This test performs the following sequence of actions, where all messages @@ -1386,7 +1414,7 @@ void CheckMessageAfterClosed(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckLostResponseWithPiggyback(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckLostResponseWithPiggyback(nlTestSuite * inSuite, void * inContext) { /** * This tests the following scenario: @@ -1540,7 +1568,125 @@ void CheckLostResponseWithPiggyback(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckLostStandaloneAck(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator(nlTestSuite * inSuite, void * inContext) +{ + /** + * This tests the following scenario: + * 1) A reliable message expecting a response is sent from the initiator to responder which is losts + * 2) Initiator resends the message a the IdleRetrans interval + * 3) Responder receives the message and sends a standalone ack + * 4) Responder sends a response and fails + * 5) Responder retries at the ActiveRestrans interval + * 6) Initiator receives the response + */ + + TestContext & ctx = *reinterpret_cast(inContext); + + chip::System::PacketBufferHandle buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); + NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + + CHIP_ERROR err = CHIP_NO_ERROR; + + MockAppDelegate mockReceiver(ctx); + err = ctx.GetExchangeManager().RegisterUnsolicitedMessageHandlerForType(Echo::MsgType::EchoRequest, &mockReceiver); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + mockReceiver.mTestSuite = inSuite; + + MockAppDelegate mockSender(ctx); + ExchangeContext * exchange = ctx.NewExchangeToAlice(&mockSender); + NL_TEST_ASSERT(inSuite, exchange != nullptr); + + mockSender.mTestSuite = inSuite; + + exchange->GetSessionHandle()->AsSecureSession()->SetRemoteMRPConfig({ + 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL + 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + }); + + ReliableMessageMgr * rm = ctx.GetExchangeManager().GetReliableMessageMgr(); + NL_TEST_ASSERT(inSuite, rm != nullptr); + + // Ensure the retransmit table is empty right now + NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); + + auto & loopback = ctx.GetLoopback(); + loopback.mSentMessageCount = 0; + loopback.mNumMessagesToDrop = 1; + loopback.mDroppedMessageCount = 0; + + mockReceiver.mRetainExchange = true; + mockSender.mRetainExchange = true; + + NL_TEST_ASSERT(inSuite, !exchange->ShouldPeerBeActive()); + + err = exchange->SendMessage(Echo::MsgType::EchoRequest, std::move(buffer), SendFlags(SendMessageFlags::kExpectResponse)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + // Verify that the first message is dropped + NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 1); + NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); + NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); + + // Make sure retransmit was not done before the idle restrans interval hits + ctx.GetIOContext().DriveIOUntil(500_ms32, [&] { return loopback.mSentMessageCount >= 1; }); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(inSuite, !exchange->ShouldPeerBeActive()); + + // // Make sure nothing happened + NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 1); + NL_TEST_ASSERT(inSuite, !mockReceiver.IsOnMessageReceivedCalled); + + // // Retrasnmit message + ctx.GetIOContext().DriveIOUntil(2000_ms32, [&] { return loopback.mSentMessageCount >= 2; }); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(inSuite, !exchange->ShouldPeerBeActive()); + + // // Make sure nothing happened + NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 2); + NL_TEST_ASSERT(inSuite, mockReceiver.IsOnMessageReceivedCalled); + + // // Verify that the receiver considers the sender is active + NL_TEST_ASSERT(inSuite, !exchange->ShouldPeerBeActive()); + NL_TEST_ASSERT(inSuite, mockReceiver.mExchange->ShouldPeerBeActive()); + + mockReceiver.mExchange->GetSessionHandle()->AsSecureSession()->SetRemoteMRPConfig({ + 1000_ms32, // CHIP_CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL + 100_ms32, // CHIP_CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL + }); + + mockReceiver.mRetainExchange = false; + mockSender.mRetainExchange = false; + + // Now send a message from the other side. + buffer = chip::MessagePacketBuffer::NewWithData(PAYLOAD, sizeof(PAYLOAD)); + NL_TEST_ASSERT(inSuite, !buffer.IsNull()); + + // Make receiver message fail once + loopback.mNumMessagesToDrop = 1; + + err = mockReceiver.mExchange->SendMessage(Echo::MsgType::EchoResponse, std::move(buffer)); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + ctx.DrainAndServiceIO(); + + // Make sure nothing happened + NL_TEST_ASSERT(inSuite, loopback.mDroppedMessageCount == 2); + NL_TEST_ASSERT(inSuite, loopback.mNumMessagesToDrop == 0); + NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 3); + NL_TEST_ASSERT(inSuite, !mockSender.IsOnMessageReceivedCalled); + + // // Retrasnmit message + ctx.GetIOContext().DriveIOUntil(150_ms32, [&] { return loopback.mSentMessageCount >= 4; }); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(inSuite, mockSender.IsOnMessageReceivedCalled); + NL_TEST_ASSERT(inSuite, loopback.mSentMessageCount == 5); +} + +void TestReliableMessageProtocol::CheckLostStandaloneAck(nlTestSuite * inSuite, void * inContext) { /** * This tests the following scenario: @@ -1668,7 +1814,7 @@ void CheckLostStandaloneAck(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, rm->TestGetCountRetransTable() == 0); } -void CheckGetBackoff(nlTestSuite * inSuite, void * inContext) +void TestReliableMessageProtocol::CheckGetBackoff(nlTestSuite * inSuite, void * inContext) { // Run 3x iterations to thoroughly test random jitter always results in backoff within bounds. for (uint32_t j = 0; j < 3; j++) @@ -1685,7 +1831,7 @@ void CheckGetBackoff(nlTestSuite * inSuite, void * inContext) } } -int InitializeTestCase(void * inContext) +int TestReliableMessageProtocol::InitializeTestCase(void * inContext) { TestContext & ctx = *static_cast(inContext); ctx.GetSessionAliceToBob()->AsSecureSession()->SetRemoteMRPConfig(GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); @@ -1709,31 +1855,32 @@ int InitializeTestCase(void * inContext) */ const nlTest sTests[] = { - NL_TEST_DEF("Test ReliableMessageMgr::CheckAddClearRetrans", CheckAddClearRetrans), - NL_TEST_DEF("Test ReliableMessageMgr::CheckResendApplicationMessage", CheckResendApplicationMessage), + NL_TEST_DEF("Test ReliableMessageMgr::CheckAddClearRetrans", TestReliableMessageProtocol::CheckAddClearRetrans), + NL_TEST_DEF("Test ReliableMessageMgr::CheckResendApplicationMessage", TestReliableMessageProtocol::CheckResendApplicationMessage), NL_TEST_DEF("Test ReliableMessageMgr::CheckCloseExchangeAndResendApplicationMessage", - CheckCloseExchangeAndResendApplicationMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckFailedMessageRetainOnSend", CheckFailedMessageRetainOnSend), + TestReliableMessageProtocol::CheckCloseExchangeAndResendApplicationMessage), + NL_TEST_DEF("Test ReliableMessageMgr::CheckFailedMessageRetainOnSend",TestReliableMessageProtocol::CheckFailedMessageRetainOnSend), NL_TEST_DEF("Test ReliableMessageMgr::CheckResendApplicationMessageWithPeerExchange", - CheckResendApplicationMessageWithPeerExchange), + TestReliableMessageProtocol::CheckResendApplicationMessageWithPeerExchange), NL_TEST_DEF("Test ReliableMessageMgr::CheckResendSessionEstablishmentMessageWithPeerExchange", - CheckResendSessionEstablishmentMessageWithPeerExchange), - NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateMessage", CheckDuplicateMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateMessageClosedExchange", CheckDuplicateMessageClosedExchange), - NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateOldMessageClosedExchange", CheckDuplicateOldMessageClosedExchange), - NL_TEST_DEF("Test that a reply after a standalone ack comes through correctly", CheckReceiveAfterStandaloneAck), + TestReliableMessageProtocol::CheckResendSessionEstablishmentMessageWithPeerExchange), + NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateMessage", TestReliableMessageProtocol::CheckDuplicateMessage), + NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateMessageClosedExchange", TestReliableMessageProtocol::CheckDuplicateMessageClosedExchange), + NL_TEST_DEF("Test ReliableMessageMgr::CheckDuplicateOldMessageClosedExchange", TestReliableMessageProtocol::CheckDuplicateOldMessageClosedExchange), + NL_TEST_DEF("Test that a reply after a standalone ack comes through correctly", TestReliableMessageProtocol::CheckReceiveAfterStandaloneAck), NL_TEST_DEF("Test that a reply to a non-MRP message piggybacks an ack if there were MRP things happening on the context before", - CheckPiggybackAfterPiggyback), - NL_TEST_DEF("Test sending an unsolicited ack-soliciting 'standalone ack' message", CheckSendUnsolicitedStandaloneAckMessage), - NL_TEST_DEF("Test ReliableMessageMgr::CheckSendStandaloneAckMessage", CheckSendStandaloneAckMessage), + TestReliableMessageProtocol::CheckPiggybackAfterPiggyback), + NL_TEST_DEF("Test sending an unsolicited ack-soliciting 'standalone ack' message", TestReliableMessageProtocol::CheckSendUnsolicitedStandaloneAckMessage), + NL_TEST_DEF("Test ReliableMessageMgr::CheckSendStandaloneAckMessage", TestReliableMessageProtocol::CheckSendStandaloneAckMessage), NL_TEST_DEF("Test command, response, default response, with receiver closing exchange after sending response", - CheckMessageAfterClosed), - NL_TEST_DEF("Test that unencrypted message is dropped if exchange requires encryption", CheckUnencryptedMessageReceiveFailure), + TestReliableMessageProtocol::CheckMessageAfterClosed), + NL_TEST_DEF("Test that unencrypted message is dropped if exchange requires encryption", TestReliableMessageProtocol::CheckUnencryptedMessageReceiveFailure), NL_TEST_DEF("Test that dropping an application-level message with a piggyback ack works ok once both sides retransmit", - CheckLostResponseWithPiggyback), + TestReliableMessageProtocol::CheckLostResponseWithPiggyback), NL_TEST_DEF("Test that an application-level response-to-response after a lost standalone ack to the initial message works", - CheckLostStandaloneAck), - NL_TEST_DEF("Test MRP backoff algorithm", CheckGetBackoff), + TestReliableMessageProtocol::CheckLostStandaloneAck), + NL_TEST_DEF("Test Is Peer Active Retry logic", TestReliableMessageProtocol::CheckIsPeerActiveNotInitiator), + NL_TEST_DEF("Test MRP backoff algorithm", TestReliableMessageProtocol::CheckGetBackoff), NL_TEST_SENTINEL(), }; @@ -1743,12 +1890,10 @@ nlTestSuite sSuite = { &sTests[0], TestContext::Initialize, TestContext::Finalize, - InitializeTestCase, + TestReliableMessageProtocol::InitializeTestCase, }; // clang-format on -} // namespace - int TestReliableMessageProtocol() { return chip::ExecuteTestsWithContext(&sSuite); diff --git a/src/protocols/bdx/TransferFacilitator.cpp b/src/protocols/bdx/TransferFacilitator.cpp index f5deb2ea5bd2c4..19d5ca051687aa 100644 --- a/src/protocols/bdx/TransferFacilitator.cpp +++ b/src/protocols/bdx/TransferFacilitator.cpp @@ -54,6 +54,7 @@ CHIP_ERROR TransferFacilitator::OnMessageReceived(chip::Messaging::ExchangeConte // For this reason, it is left up to the application logic to call ExchangeContext::Close() when it has determined that the // transfer is finished. mExchangeCtx->WillSendMessage(); + mExchangeCtx->SetShouldPeerBeActive(true); return err; } diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 2afc40feacaf02..9e6706cc616b9b 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -1410,6 +1410,7 @@ CHIP_ERROR CASESession::SendSigma3a() SuccessOrExit(err = helper->ScheduleWork()); mSendSigma3Helper = helper; mExchangeCtxt->WillSendMessage(); + mExchangeCtxt->SetShouldPeerBeActive(true); // When we send sigma3, peer has processed sigma 1 and sent sigma2 which means it is active mState = State::kSendSigma3Pending; } else @@ -1703,6 +1704,7 @@ CHIP_ERROR CASESession::HandleSigma3a(System::PacketBufferHandle && msg) SuccessOrExit(err = helper->ScheduleWork()); mHandleSigma3Helper = helper; mExchangeCtxt->WillSendMessage(); + mExchangeCtxt->SetShouldPeerBeActive(true); // When we receive sigma3, peer has received and processed sigma2 which means it is active mState = State::kHandleSigma3Pending; } diff --git a/src/protocols/secure_channel/PASESession.cpp b/src/protocols/secure_channel/PASESession.cpp index 3ddf7ae3882e53..5a0e004f0d6114 100644 --- a/src/protocols/secure_channel/PASESession.cpp +++ b/src/protocols/secure_channel/PASESession.cpp @@ -825,6 +825,9 @@ CHIP_ERROR PASESession::OnMessageReceived(ExchangeContext * exchange, const Payl MsgType msgType = static_cast(payloadHeader.GetMessageType()); SuccessOrExit(err); + // Once we receive a message for the PASE protocol, we know the Peer is active + mExchangeCtxt->SetShouldPeerBeActive(true); + #if CHIP_CONFIG_SLOW_CRYPTO if (msgType == MsgType::PBKDFParamRequest || msgType == MsgType::PBKDFParamResponse || msgType == MsgType::PASE_Pake1 || msgType == MsgType::PASE_Pake2 || msgType == MsgType::PASE_Pake3)