diff --git a/src/app/tests/suites/TestDiagnosticLogs.yaml b/src/app/tests/suites/TestDiagnosticLogs.yaml index cf31a96b8ddf58..57ab8cb0fa04f8 100644 --- a/src/app/tests/suites/TestDiagnosticLogs.yaml +++ b/src/app/tests/suites/TestDiagnosticLogs.yaml @@ -18,6 +18,7 @@ config: nodeId: 0x12344321 cluster: "Diagnostic Logs" endpoint: 0 + timeout: 120 end_user_support_log_file_path: "/tmp/end_user_support_log.txt" end_user_support_log_file_content: "End User Support Log Content" end_user_support_log_file_content_long: @@ -31,6 +32,113 @@ config: bdx_transfer_file_path: "/tmp/end_user_support_bdx_output.txt" bdx_transfer_file_name: "end_user_support_bdx_output.txt" + bdx_transfer_file_path_1: "/tmp/bdx_log_output_1.txt" + bdx_transfer_file_name_1: "bdx_log_output_1.txt" + bdx_transfer_file_path_2: "/tmp/bdx_log_output_2.txt" + bdx_transfer_file_name_2: "bdx_log_output_2.txt" + + long_log_file_content: + "This is a long log content... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ....................................................... + ......................................................." + tests: # # Set up the test by adding some destination log files for the target accessory: @@ -658,3 +766,180 @@ tests: values: - name: "filePath" value: bdx_transfer_file_path + + # + # Validate that multiple BDX transfers can run in parallels. + # + - label: "Delete possible leftover from previous run" + cluster: "SystemCommands" + command: "DeleteFile" + arguments: + values: + - name: "filePath" + value: bdx_transfer_file_path_1 + + - label: "Delete possible leftover from previous run" + cluster: "SystemCommands" + command: "DeleteFile" + arguments: + values: + - name: "filePath" + value: bdx_transfer_file_path_2 + + - label: "Update End User Support logs with a long log" + cluster: "SystemCommands" + command: "CreateFile" + arguments: + values: + - name: "filePath" + value: end_user_support_log_file_path + - name: "fileContent" + value: long_log_file_content + + - label: "Update Network Diagnostic logs with a long log" + cluster: "SystemCommands" + command: "CreateFile" + arguments: + values: + - name: "filePath" + value: network_diagnostics_log_file_path + - name: "fileContent" + value: long_log_file_content + + - label: "Start a second accessory" + cluster: "SystemCommands" + command: "Start" + arguments: + values: + - name: "discriminator" + value: 50 + - name: "port" + value: 5601 + - name: "kvs" + value: "/tmp/chip_kvs_second" + - name: "endUserSupportLogPath" + value: end_user_support_log_file_path + - name: "networkDiagnosticsLogPath" + value: network_diagnostics_log_file_path + - name: "registerKey" + value: "default#2" + + - label: "Commission second accessory from beta" + identity: "beta" + cluster: "CommissionerCommands" + command: "PairWithCode" + arguments: + values: + - name: "nodeId" + value: nodeId + - name: "payload" + value: "MT:-24J0IX4122-.548G00" + + - label: "Wait for the second commissioned device to be retrieved for beta" + identity: "beta" + cluster: "DelayCommands" + command: "WaitForCommissionee" + arguments: + values: + - name: "nodeId" + value: nodeId + + - label: "BDX: Request End User Support from the first accessory" + command: "RetrieveLogsRequest" + identity: "alpha" + arguments: + values: + - name: "Intent" + value: 1 # NetworkDiagnostics + - name: "RequestedProtocol" + value: 1 # BDX + - name: "TransferFileDesignator" + value: bdx_transfer_file_name_1 + response: + values: + - name: "Status" + value: 0 # Success + - name: "LogContent" + value: "" + + - label: "BDX: Request End User Support from the second accessory" + command: "RetrieveLogsRequest" + identity: "beta" + arguments: + values: + - name: "Intent" + value: 1 # NetworkDiagnostics + - name: "RequestedProtocol" + value: 1 # BDX + - name: "TransferFileDesignator" + value: bdx_transfer_file_name_2 + response: + values: + - name: "Status" + value: 0 # Success + - name: "LogContent" + value: "" + + - label: + "BDX: Wait for 'Diagnostic logs transfer: Success' message from the + first accessory" + cluster: "DelayCommands" + command: "WaitForMessage" + arguments: + values: + - name: "registerKey" + value: "default" + - name: "message" + value: "Diagnostic logs transfer: Success" + + - label: + "BDX: Wait for 'Diagnostic logs transfer: Success' message from the + second accessory" + cluster: "DelayCommands" + command: "WaitForMessage" + arguments: + values: + - name: "registerKey" + value: "default#2" + - name: "message" + value: "Diagnostic logs transfer: Success" + + - label: + "Compare the content the original log and the file that has been + created as the result of the BDX transfer" + cluster: "SystemCommands" + command: "CompareFiles" + arguments: + values: + - name: "file1" + value: end_user_support_log_file_path + - name: "file2" + value: bdx_transfer_file_path_1 + + - label: + "Compare the content the original log and the file that has been + created as the result of the BDX transfer" + cluster: "SystemCommands" + command: "CompareFiles" + arguments: + values: + - name: "file1" + value: network_diagnostics_log_file_path + - name: "file2" + value: bdx_transfer_file_path_2 + + - label: "Delete the result of the previous run" + cluster: "SystemCommands" + command: "DeleteFile" + arguments: + values: + - name: "filePath" + value: bdx_transfer_file_path_1 + + - label: "Delete the result of the previous run" + cluster: "SystemCommands" + command: "DeleteFile" + arguments: + values: + - name: "filePath" + value: bdx_transfer_file_path_2 diff --git a/src/controller/CHIPDeviceControllerFactory.cpp b/src/controller/CHIPDeviceControllerFactory.cpp index 8268b07b7f3dcc..ebc728ca206875 100644 --- a/src/controller/CHIPDeviceControllerFactory.cpp +++ b/src/controller/CHIPDeviceControllerFactory.cpp @@ -226,7 +226,7 @@ CHIP_ERROR DeviceControllerFactory::InitSystemState(FactoryInitParams params) ReturnErrorOnFailure(stateParams.exchangeMgr->Init(stateParams.sessionMgr)); ReturnErrorOnFailure(stateParams.messageCounterManager->Init(stateParams.exchangeMgr)); ReturnErrorOnFailure(stateParams.unsolicitedStatusHandler->Init(stateParams.exchangeMgr)); - ReturnErrorOnFailure(stateParams.bdxTransferServer->ListenForSendInit(stateParams.systemLayer, stateParams.exchangeMgr)); + ReturnErrorOnFailure(stateParams.bdxTransferServer->Init(stateParams.systemLayer, stateParams.exchangeMgr)); InitDataModelHandler(); diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index 4fa06b2548b513..a0f7378016dc01 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1731,6 +1731,17 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER 0 #endif +/** + * @def CHIP_CONFIG_MAX_BDX_LOG_TRANSFERS + * + * @brief + * Maximum number of simultaneously active bdx log transfers. + * + */ +#ifndef CHIP_CONFIG_MAX_BDX_LOG_TRANSFERS +#define CHIP_CONFIG_MAX_BDX_LOG_TRANSFERS 5 +#endif // CHIP_CONFIG_MAX_BDX_LOG_TRANSFERS + /** * @} */ diff --git a/src/protocols/bdx/BUILD.gn b/src/protocols/bdx/BUILD.gn index c78b91e9d907a8..1a44b7bf5b713e 100644 --- a/src/protocols/bdx/BUILD.gn +++ b/src/protocols/bdx/BUILD.gn @@ -20,6 +20,9 @@ static_library("bdx") { sources = [ "BdxMessages.cpp", "BdxMessages.h", + "BdxTransferDiagnosticLog.cpp", + "BdxTransferDiagnosticLog.h", + "BdxTransferDiagnosticLogPool.h", "BdxTransferProxy.h", "BdxTransferProxyDiagnosticLog.cpp", "BdxTransferProxyDiagnosticLog.h", diff --git a/src/protocols/bdx/BdxTransferDiagnosticLog.cpp b/src/protocols/bdx/BdxTransferDiagnosticLog.cpp new file mode 100644 index 00000000000000..8b349fed31e8f5 --- /dev/null +++ b/src/protocols/bdx/BdxTransferDiagnosticLog.cpp @@ -0,0 +1,198 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#include "BdxTransferDiagnosticLog.h" + +namespace chip { +namespace bdx { + +namespace { +// Max block size for the BDX transfer. +constexpr uint32_t kMaxBdxBlockSize = 1024; + +// Timeout for the BDX transfer session.. +constexpr System::Clock::Timeout kBdxTimeout = System::Clock::Seconds16(5 * 60); +constexpr TransferRole kBdxRole = TransferRole::kReceiver; +} // namespace + +void BdxTransferDiagnosticLog::HandleTransferSessionOutput(TransferSession::OutputEvent & event) +{ + assertChipStackLockedByCurrentThread(); + + ChipLogDetail(BDX, "Got an event %s", event.ToString(event.EventType)); + + switch (event.EventType) + { + case TransferSession::OutputEventType::kInitReceived: + AbortTransferOnFailure(OnTransferSessionBegin(event)); + break; + case TransferSession::OutputEventType::kStatusReceived: + ChipLogError(BDX, "Got StatusReport %x", static_cast(event.statusData.statusCode)); + LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); + break; + case TransferSession::OutputEventType::kInternalError: + LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); + break; + case TransferSession::OutputEventType::kTransferTimeout: + LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_TIMEOUT)); + break; + case TransferSession::OutputEventType::kBlockReceived: + AbortTransferOnFailure(OnBlockReceived(event)); + break; + case TransferSession::OutputEventType::kMsgToSend: + LogErrorOnFailure(OnMessageToSend(event)); + + if (event.msgTypeData.HasMessageType(MessageType::BlockAckEOF)) + { + LogErrorOnFailure(OnTransferSessionEnd(CHIP_NO_ERROR)); + } + break; + case TransferSession::OutputEventType::kAckEOFReceived: + case TransferSession::OutputEventType::kNone: + case TransferSession::OutputEventType::kQueryWithSkipReceived: + case TransferSession::OutputEventType::kQueryReceived: + case TransferSession::OutputEventType::kAckReceived: + case TransferSession::OutputEventType::kAcceptReceived: + // Nothing to do. + break; + default: + // Should never happen. + chipDie(); + break; + } +} + +CHIP_ERROR BdxTransferDiagnosticLog::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, + System::PacketBufferHandle && payload) +{ + assertChipStackLockedByCurrentThread(); + + VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INCORRECT_STATE); + + // If we receive a SendInit message, then we prepare for transfer + if (payloadHeader.HasMessageType(MessageType::SendInit)) + { + FabricIndex fabricIndex = ec->GetSessionHandle()->GetFabricIndex(); + NodeId peerNodeId = ec->GetSessionHandle()->GetPeer().GetNodeId(); + VerifyOrReturnError(fabricIndex != kUndefinedFabricIndex, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(peerNodeId != kUndefinedNodeId, CHIP_ERROR_INVALID_ARGUMENT); + + mTransferProxy.SetFabricIndex(fabricIndex); + mTransferProxy.SetPeerNodeId(peerNodeId); + auto flags(TransferControlFlags::kSenderDrive); + ReturnLogErrorOnFailure(Responder::PrepareForTransfer(mSystemLayer, kBdxRole, flags, kMaxBdxBlockSize, kBdxTimeout)); + } + + return TransferFacilitator::OnMessageReceived(ec, payloadHeader, std::move(payload)); +} + +CHIP_ERROR BdxTransferDiagnosticLog::OnMessageToSend(TransferSession::OutputEvent & event) +{ + assertChipStackLockedByCurrentThread(); + + VerifyOrReturnError(mExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE); + + auto & msgTypeData = event.msgTypeData; + bool isStatusReport = msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport); + + // All messages sent from the Sender expect a response, except for a StatusReport which would indicate an error and + // the end of the transfer. + Messaging::SendFlags sendFlags; + VerifyOrDo(isStatusReport, sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse)); + + // If there's an error sending the message, close the exchange by calling Reset. + auto err = mExchangeCtx->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(event.MsgData), sendFlags); + VerifyOrDo(CHIP_NO_ERROR == err, OnTransferSessionEnd(err)); + + return err; +} + +CHIP_ERROR BdxTransferDiagnosticLog::OnTransferSessionBegin(TransferSession::OutputEvent & event) +{ + assertChipStackLockedByCurrentThread(); + VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); + + ReturnErrorOnFailure(mTransferProxy.Init(&mTransfer)); + return mDelegate->OnTransferBegin(&mTransferProxy); +} + +CHIP_ERROR BdxTransferDiagnosticLog::OnTransferSessionEnd(CHIP_ERROR error) +{ + assertChipStackLockedByCurrentThread(); + VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); + + LogErrorOnFailure(mDelegate->OnTransferEnd(&mTransferProxy, error)); + Reset(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR BdxTransferDiagnosticLog::OnBlockReceived(TransferSession::OutputEvent & event) +{ + assertChipStackLockedByCurrentThread(); + VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); + + ByteSpan blockData(event.blockdata.Data, event.blockdata.Length); + return mDelegate->OnTransferData(&mTransferProxy, blockData); +} + +void BdxTransferDiagnosticLog::AbortTransferOnFailure(CHIP_ERROR error) +{ + VerifyOrReturn(CHIP_NO_ERROR != error); + LogErrorOnFailure(error); + LogErrorOnFailure(mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(error))); +} + +void BdxTransferDiagnosticLog::Reset() +{ + assertChipStackLockedByCurrentThread(); + + Responder::ResetTransfer(); + + if (mExchangeCtx) + { + mIsExchangeClosing = true; + mExchangeCtx->Close(); + mIsExchangeClosing = false; + mExchangeCtx = nullptr; + } + + mTransferProxy.Reset(); +} + +void BdxTransferDiagnosticLog::OnExchangeClosing(Messaging::ExchangeContext * ec) +{ + // The exchange can be closing while TransferFacilitator is still accessing us, so + // the BdxTransferDiagnosticLog can not be released "right now". + mSystemLayer->ScheduleWork( + [](auto * systemLayer, auto * appState) -> void { + auto * _this = static_cast(appState); + _this->mPoolDelegate->Release(_this); + }, + this); + + // This block checks and handles the scenario where the exchange is closed externally (e.g., receiving a StatusReport). + // Continuing to use it could lead to a use-after-free error and such an error might occur when the poll timer triggers and + // OnTransferSessionEnd is called. + // We know it's not just us normally closing the exchange if mIsExchangeClosing is false. + VerifyOrReturn(!mIsExchangeClosing); + mExchangeCtx = nullptr; + LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); +} + +} // namespace bdx +} // namespace chip diff --git a/src/protocols/bdx/BdxTransferDiagnosticLog.h b/src/protocols/bdx/BdxTransferDiagnosticLog.h new file mode 100644 index 00000000000000..b8301b23da0451 --- /dev/null +++ b/src/protocols/bdx/BdxTransferDiagnosticLog.h @@ -0,0 +1,111 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace chip { +namespace bdx { + +class BdxTransferDiagnosticLog : public Responder +{ +public: + BdxTransferDiagnosticLog(BDXTransferServerDelegate * delegate, BdxTransferDiagnosticLogPoolDelegate * poolDelegate, + System::Layer * systemLayer) : + mSystemLayer(systemLayer), + mDelegate(delegate), mPoolDelegate(poolDelegate){}; + + ~BdxTransferDiagnosticLog(){ Reset(); }; + + /** + * This method handles BDX messages and other TransferSession events. + * + * @param[in] event An OutputEvent that contains output from the TransferSession object. + */ + void HandleTransferSessionOutput(TransferSession::OutputEvent & event) override; + + void OnExchangeClosing(Messaging::ExchangeContext * ec) override; + +protected: + /** + * Called when a BDX message is received over the exchange context + * + * @param[in] ec The exchange context + * + * @param[in] payloadHeader The payload header of the message + * + * @param[in] payload The payload of the message + */ + CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, + System::PacketBufferHandle && payload) override; + +private: + /** + * Called to send a BDX MsgToSend message over the exchange + * + * + * @param[in] event The output event to be send + */ + CHIP_ERROR OnMessageToSend(TransferSession::OutputEvent & event); + + /** + * Called to begin the transfer session when an init message has been received + * + * @param[in] event The output event received + */ + CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event); + + /** + * Called to end the transfer session when a BlockAckEOF message has been sent over the exchange + * or an error has occurred during the BDX session + * + * @param[in] error The error type + */ + CHIP_ERROR OnTransferSessionEnd(CHIP_ERROR error); + + /** + * Called when a block has been received from the Sender. The block is processed + * and written to a file and a block ack is sent back to the sender. + * + * @param[in] event The output event received + */ + CHIP_ERROR OnBlockReceived(TransferSession::OutputEvent & event); + + /** + * This method is called to reset state. It resets the transfer and cleans up the + * exchange and the fabric index and peer node id. + */ + void Reset(); + + void AbortTransferOnFailure(CHIP_ERROR error); + + BDXTransferProxyDiagnosticLog mTransferProxy; + bool mIsExchangeClosing = false; + + System::Layer * mSystemLayer; + + BDXTransferServerDelegate * mDelegate; + BdxTransferDiagnosticLogPoolDelegate * mPoolDelegate; +}; + +} // namespace bdx +} // namespace chip diff --git a/src/protocols/bdx/BdxTransferDiagnosticLogPool.h b/src/protocols/bdx/BdxTransferDiagnosticLogPool.h new file mode 100644 index 00000000000000..a2d7d3f5d11105 --- /dev/null +++ b/src/protocols/bdx/BdxTransferDiagnosticLogPool.h @@ -0,0 +1,58 @@ +/* + * + * Copyright (c) 2024 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. + */ + +#pragma once + +#include +#include +#include + +namespace chip { +namespace bdx { + +class BdxTransferDiagnosticLog; + +class BdxTransferDiagnosticLogPoolDelegate +{ +public: + virtual ~BdxTransferDiagnosticLogPoolDelegate() {} + + virtual BdxTransferDiagnosticLog * Allocate(BDXTransferServerDelegate * delegate, System::Layer * systemLayer) = 0; + + virtual void Release(BdxTransferDiagnosticLog * client) = 0; +}; + +template +class BdxTransferDiagnosticLogPool : public BdxTransferDiagnosticLogPoolDelegate +{ +public: + ~BdxTransferDiagnosticLogPool() override { mTransferPool.ReleaseAll(); } + + BdxTransferDiagnosticLog * Allocate(BDXTransferServerDelegate * delegate, System::Layer * systemLayer) override + { + return mTransferPool.CreateObject(delegate, this, systemLayer); + } + + void Release(BdxTransferDiagnosticLog * transfer) override { mTransferPool.ReleaseObject(transfer); } + +private: + ObjectPool mTransferPool; +}; + +} // namespace bdx +} // namespace chip diff --git a/src/protocols/bdx/BdxTransferServer.cpp b/src/protocols/bdx/BdxTransferServer.cpp index b276dba51ab521..709d21b1f3b835 100644 --- a/src/protocols/bdx/BdxTransferServer.cpp +++ b/src/protocols/bdx/BdxTransferServer.cpp @@ -21,16 +21,7 @@ namespace chip { namespace bdx { -namespace { -// Max block size for the BDX transfer. -constexpr uint32_t kMaxBdxBlockSize = 1024; - -// Timeout for the BDX transfer session.. -constexpr System::Clock::Timeout kBdxTimeout = System::Clock::Seconds16(5 * 60); -constexpr TransferRole kBdxRole = TransferRole::kReceiver; -} // namespace - -CHIP_ERROR BDXTransferServer::ListenForSendInit(System::Layer * systemLayer, Messaging::ExchangeManager * exchangeMgr) +CHIP_ERROR BDXTransferServer::Init(System::Layer * systemLayer, Messaging::ExchangeManager * exchangeMgr) { VerifyOrReturnError(nullptr != systemLayer, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(nullptr != exchangeMgr, CHIP_ERROR_INVALID_ARGUMENT); @@ -40,170 +31,31 @@ CHIP_ERROR BDXTransferServer::ListenForSendInit(System::Layer * systemLayer, Mes return mExchangeMgr->RegisterUnsolicitedMessageHandlerForType(MessageType::SendInit, this); } -void BDXTransferServer::Shutdown() -{ - VerifyOrReturn(nullptr != mSystemLayer); - VerifyOrReturn(nullptr != mExchangeMgr); - - LogErrorOnFailure(mExchangeMgr->UnregisterUnsolicitedMessageHandlerForType(MessageType::SendInit)); - - mSystemLayer = nullptr; - mExchangeMgr = nullptr; -} - -void BDXTransferServer::HandleTransferSessionOutput(TransferSession::OutputEvent & event) -{ - assertChipStackLockedByCurrentThread(); - - ChipLogDetail(BDX, "Got an event %s", event.ToString(event.EventType)); - - switch (event.EventType) - { - case TransferSession::OutputEventType::kInitReceived: - AbortTransferOnFailure(OnTransferSessionBegin(event)); - break; - case TransferSession::OutputEventType::kStatusReceived: - ChipLogError(BDX, "Got StatusReport %x", static_cast(event.statusData.statusCode)); - LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); - break; - case TransferSession::OutputEventType::kInternalError: - LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); - break; - case TransferSession::OutputEventType::kTransferTimeout: - LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_TIMEOUT)); - break; - case TransferSession::OutputEventType::kBlockReceived: - AbortTransferOnFailure(OnBlockReceived(event)); - break; - case TransferSession::OutputEventType::kMsgToSend: - LogErrorOnFailure(OnMessageToSend(event)); - - if (event.msgTypeData.HasMessageType(MessageType::BlockAckEOF)) - { - LogErrorOnFailure(OnTransferSessionEnd(CHIP_NO_ERROR)); - } - break; - case TransferSession::OutputEventType::kAckEOFReceived: - case TransferSession::OutputEventType::kNone: - case TransferSession::OutputEventType::kQueryWithSkipReceived: - case TransferSession::OutputEventType::kQueryReceived: - case TransferSession::OutputEventType::kAckReceived: - case TransferSession::OutputEventType::kAcceptReceived: - // Nothing to do. - break; - default: - // Should never happen. - chipDie(); - break; - } -} - -CHIP_ERROR BDXTransferServer::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, - System::PacketBufferHandle && payload) -{ - assertChipStackLockedByCurrentThread(); - - VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INCORRECT_STATE); - - // If we receive a SendInit message, then we prepare for transfer - if (payloadHeader.HasMessageType(MessageType::SendInit)) - { - FabricIndex fabricIndex = ec->GetSessionHandle()->GetFabricIndex(); - NodeId peerNodeId = ec->GetSessionHandle()->GetPeer().GetNodeId(); - VerifyOrReturnError(fabricIndex != kUndefinedFabricIndex, CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(peerNodeId != kUndefinedNodeId, CHIP_ERROR_INVALID_ARGUMENT); - - mTransferProxy.SetFabricIndex(fabricIndex); - mTransferProxy.SetPeerNodeId(peerNodeId); - auto flags(TransferControlFlags::kSenderDrive); - ReturnLogErrorOnFailure(Responder::PrepareForTransfer(mSystemLayer, kBdxRole, flags, kMaxBdxBlockSize, kBdxTimeout)); - } - - return TransferFacilitator::OnMessageReceived(ec, payloadHeader, std::move(payload)); -} - -CHIP_ERROR BDXTransferServer::OnMessageToSend(TransferSession::OutputEvent & event) +CHIP_ERROR BDXTransferServer::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, + Messaging::ExchangeDelegate *& newDelegate) { - assertChipStackLockedByCurrentThread(); + auto * logTransfer = mPoolDelegate.Allocate(mDelegate, mSystemLayer); + VerifyOrReturnError(nullptr != logTransfer, CHIP_ERROR_NO_MEMORY); - VerifyOrReturnError(mExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE); - - auto & msgTypeData = event.msgTypeData; - bool isStatusReport = msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport); - - // All messages sent from the Sender expect a response, except for a StatusReport which would indicate an error and - // the end of the transfer. - Messaging::SendFlags sendFlags; - VerifyOrDo(isStatusReport, sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse)); - - // If there's an error sending the message, close the exchange by calling Reset. - auto err = mExchangeCtx->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(event.MsgData), sendFlags); - VerifyOrDo(CHIP_NO_ERROR == err, Reset()); - - return err; -} - -CHIP_ERROR BDXTransferServer::OnTransferSessionBegin(TransferSession::OutputEvent & event) -{ - assertChipStackLockedByCurrentThread(); - VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); - - ReturnErrorOnFailure(mTransferProxy.Init(&mTransfer)); - return mDelegate->OnTransferBegin(&mTransferProxy); -} - -CHIP_ERROR BDXTransferServer::OnTransferSessionEnd(CHIP_ERROR error) -{ - assertChipStackLockedByCurrentThread(); - VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); - - LogErrorOnFailure(mDelegate->OnTransferEnd(&mTransferProxy, error)); - Reset(); + newDelegate = logTransfer; return CHIP_NO_ERROR; } -CHIP_ERROR BDXTransferServer::OnBlockReceived(TransferSession::OutputEvent & event) -{ - assertChipStackLockedByCurrentThread(); - VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); - - ByteSpan blockData(event.blockdata.Data, event.blockdata.Length); - return mDelegate->OnTransferData(&mTransferProxy, blockData); -} - -void BDXTransferServer::AbortTransferOnFailure(CHIP_ERROR error) +void BDXTransferServer::OnExchangeCreationFailed(Messaging::ExchangeDelegate * delegate) { - VerifyOrReturn(CHIP_NO_ERROR != error); - LogErrorOnFailure(error); - LogErrorOnFailure(mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(error))); + auto * logTransfer = static_cast(delegate); + mPoolDelegate.Release(logTransfer); } -void BDXTransferServer::Reset() +void BDXTransferServer::Shutdown() { - assertChipStackLockedByCurrentThread(); - - Responder::ResetTransfer(); - - if (mExchangeCtx) - { - mIsExchangeClosing = true; - mExchangeCtx->Close(); - mIsExchangeClosing = false; - mExchangeCtx = nullptr; - } + VerifyOrReturn(nullptr != mSystemLayer); + VerifyOrReturn(nullptr != mExchangeMgr); - mTransferProxy.Reset(); -} + LogErrorOnFailure(mExchangeMgr->UnregisterUnsolicitedMessageHandlerForType(MessageType::SendInit)); -void BDXTransferServer::OnExchangeClosing(Messaging::ExchangeContext * ec) -{ - // This block checks and handles the scenario where the exchange is closed externally (e.g., receiving a StatusReport). - // Continuing to use it could lead to a use-after-free error and such an error might occur when the poll timer triggers and - // OnTransferSessionEnd is called. - // We know it's not just us normally closing the exchange if mIsExchangeClosing is false. - VerifyOrReturn(!mIsExchangeClosing); - mExchangeCtx = nullptr; - LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); + mSystemLayer = nullptr; + mExchangeMgr = nullptr; } } // namespace bdx diff --git a/src/protocols/bdx/BdxTransferServer.h b/src/protocols/bdx/BdxTransferServer.h index 83da8b97ecb6a8..0fac0e88b672de 100644 --- a/src/protocols/bdx/BdxTransferServer.h +++ b/src/protocols/bdx/BdxTransferServer.h @@ -18,97 +18,41 @@ #pragma once -#include "BdxTransferServerDelegate.h" +#include +#include #include -#include -#include -#include +#include namespace chip { namespace bdx { -class BDXTransferServer : public Responder +class BdxTransferDiagnosticLog; + +class BDXTransferServer : public Messaging::UnsolicitedMessageHandler { public: BDXTransferServer(){}; ~BDXTransferServer() { Shutdown(); }; - CHIP_ERROR ListenForSendInit(System::Layer * systemLayer, Messaging::ExchangeManager * exchangeMgr); + CHIP_ERROR Init(System::Layer * systemLayer, Messaging::ExchangeManager * exchangeMgr); void Shutdown(); void SetDelegate(BDXTransferServerDelegate * delegate) { mDelegate = delegate; } - /** - * This method handles BDX messages and other TransferSession events. - * - * @param[in] event An OutputEvent that contains output from the TransferSession object. - */ - void HandleTransferSessionOutput(TransferSession::OutputEvent & event) override; - - void OnExchangeClosing(Messaging::ExchangeContext * ec) override; - protected: - /** - * Called when a BDX message is received over the exchange context - * - * @param[in] ec The exchange context - * - * @param[in] payloadHeader The payload header of the message - * - * @param[in] payload The payload of the message - */ - CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, - System::PacketBufferHandle && payload) override; + CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, + Messaging::ExchangeDelegate *& newDelegate) override; + void OnExchangeCreationFailed(Messaging::ExchangeDelegate * delegate) override; private: - /** - * Called to send a BDX MsgToSend message over the exchange - * - * - * @param[in] event The output event to be send - */ - CHIP_ERROR OnMessageToSend(TransferSession::OutputEvent & event); - - /** - * Called to begin the transfer session when an init message has been received - * - * @param[in] event The output event received - */ - CHIP_ERROR OnTransferSessionBegin(TransferSession::OutputEvent & event); - - /** - * Called to end the transfer session when a BlockAckEOF message has been sent over the exchange - * or an error has occurred during the BDX session - * - * @param[in] error The error type - */ - CHIP_ERROR OnTransferSessionEnd(CHIP_ERROR error); - - /** - * Called when a block has been received from the Sender. The block is processed - * and written to a file and a block ack is sent back to the sender. - * - * @param[in] event The output event received - */ - CHIP_ERROR OnBlockReceived(TransferSession::OutputEvent & event); - - /** - * This method is called to reset state. It resets the transfer and cleans up the - * exchange and the fabric index and peer node id. - */ - void Reset(); - - void AbortTransferOnFailure(CHIP_ERROR error); - System::Layer * mSystemLayer; Messaging::ExchangeManager * mExchangeMgr; - BDXTransferServerDelegate * mDelegate; - BDXTransferProxyDiagnosticLog mTransferProxy; - bool mIsExchangeClosing = false; + BDXTransferServerDelegate * mDelegate; + BdxTransferDiagnosticLogPool mPoolDelegate; }; } // namespace bdx