Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2024 Project CHIP Authors 4 : * All rights reserved. 5 : * 6 : * Licensed under the Apache License, Version 2.0 (the "License"); 7 : * you may not use this file except in compliance with the License. 8 : * You may obtain a copy of the License at 9 : * 10 : * http://www.apache.org/licenses/LICENSE-2.0 11 : * 12 : * Unless required by applicable law or agreed to in writing, software 13 : * distributed under the License is distributed on an "AS IS" BASIS, 14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 : * See the License for the specific language governing permissions and 16 : * limitations under the License. 17 : */ 18 : 19 : #include "BdxTransferDiagnosticLog.h" 20 : 21 : namespace chip { 22 : namespace bdx { 23 : 24 : namespace { 25 : // Max block size for the BDX transfer. 26 : constexpr uint32_t kMaxBdxBlockSize = 1024; 27 : 28 : // Timeout for the BDX transfer session.. 29 : constexpr System::Clock::Timeout kBdxTimeout = System::Clock::Seconds16(5 * 60); 30 : constexpr TransferRole kBdxRole = TransferRole::kReceiver; 31 : } // namespace 32 : 33 0 : void BdxTransferDiagnosticLog::HandleTransferSessionOutput(TransferSession::OutputEvent & event) 34 : { 35 0 : assertChipStackLockedByCurrentThread(); 36 : 37 0 : ChipLogDetail(BDX, "Got an event %s", event.ToString(event.EventType)); 38 : 39 0 : switch (event.EventType) 40 : { 41 0 : case TransferSession::OutputEventType::kInitReceived: 42 0 : AbortTransferOnFailure(OnTransferSessionBegin(event)); 43 0 : break; 44 0 : case TransferSession::OutputEventType::kStatusReceived: 45 0 : ChipLogError(BDX, "Got StatusReport %x", static_cast<uint16_t>(event.statusData.statusCode)); 46 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); 47 0 : break; 48 0 : case TransferSession::OutputEventType::kInternalError: 49 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); 50 0 : break; 51 0 : case TransferSession::OutputEventType::kTransferTimeout: 52 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_TIMEOUT)); 53 0 : break; 54 0 : case TransferSession::OutputEventType::kBlockReceived: 55 0 : AbortTransferOnFailure(OnBlockReceived(event)); 56 0 : break; 57 0 : case TransferSession::OutputEventType::kMsgToSend: 58 0 : LogErrorOnFailure(OnMessageToSend(event)); 59 : 60 0 : if (event.msgTypeData.HasMessageType(MessageType::BlockAckEOF)) 61 : { 62 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_NO_ERROR)); 63 : } 64 0 : break; 65 0 : case TransferSession::OutputEventType::kAckEOFReceived: 66 : case TransferSession::OutputEventType::kNone: 67 : case TransferSession::OutputEventType::kQueryWithSkipReceived: 68 : case TransferSession::OutputEventType::kQueryReceived: 69 : case TransferSession::OutputEventType::kAckReceived: 70 : case TransferSession::OutputEventType::kAcceptReceived: 71 : // Nothing to do. 72 0 : break; 73 0 : default: 74 : // Should never happen. 75 0 : chipDie(); 76 : break; 77 : } 78 0 : } 79 : 80 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, 81 : System::PacketBufferHandle && payload) 82 : { 83 0 : assertChipStackLockedByCurrentThread(); 84 : 85 0 : VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INCORRECT_STATE); 86 : 87 : // If we receive a SendInit message, then we prepare for transfer 88 0 : if (payloadHeader.HasMessageType(MessageType::SendInit)) 89 : { 90 0 : FabricIndex fabricIndex = ec->GetSessionHandle()->GetFabricIndex(); 91 0 : NodeId peerNodeId = ec->GetSessionHandle()->GetPeer().GetNodeId(); 92 0 : VerifyOrReturnError(fabricIndex != kUndefinedFabricIndex, CHIP_ERROR_INVALID_ARGUMENT); 93 0 : VerifyOrReturnError(peerNodeId != kUndefinedNodeId, CHIP_ERROR_INVALID_ARGUMENT); 94 : 95 0 : mTransferProxy.SetFabricIndex(fabricIndex); 96 0 : mTransferProxy.SetPeerNodeId(peerNodeId); 97 0 : auto flags(TransferControlFlags::kSenderDrive); 98 0 : ReturnLogErrorOnFailure(Responder::PrepareForTransfer(mSystemLayer, kBdxRole, flags, kMaxBdxBlockSize, kBdxTimeout)); 99 : } 100 : 101 0 : return TransferFacilitator::OnMessageReceived(ec, payloadHeader, std::move(payload)); 102 : } 103 : 104 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnMessageToSend(TransferSession::OutputEvent & event) 105 : { 106 0 : assertChipStackLockedByCurrentThread(); 107 : 108 0 : VerifyOrReturnError(mExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE); 109 : 110 0 : auto & msgTypeData = event.msgTypeData; 111 0 : bool isStatusReport = msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport); 112 : 113 : // All messages sent from the Sender expect a response, except for a StatusReport which would indicate an error and 114 : // the end of the transfer. 115 0 : Messaging::SendFlags sendFlags; 116 0 : VerifyOrDo(isStatusReport, sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse)); 117 : 118 : // If there's an error sending the message, close the exchange by calling Reset. 119 0 : auto err = mExchangeCtx->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(event.MsgData), sendFlags); 120 0 : VerifyOrDo(CHIP_NO_ERROR == err, OnTransferSessionEnd(err)); 121 : 122 0 : return err; 123 : } 124 : 125 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnTransferSessionBegin(TransferSession::OutputEvent & event) 126 : { 127 0 : assertChipStackLockedByCurrentThread(); 128 0 : VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); 129 : 130 0 : ReturnErrorOnFailure(mTransferProxy.Init(&mTransfer)); 131 0 : return mDelegate->OnTransferBegin(&mTransferProxy); 132 : } 133 : 134 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnTransferSessionEnd(CHIP_ERROR error) 135 : { 136 0 : assertChipStackLockedByCurrentThread(); 137 0 : VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); 138 : 139 0 : LogErrorOnFailure(mDelegate->OnTransferEnd(&mTransferProxy, error)); 140 0 : Reset(); 141 0 : return CHIP_NO_ERROR; 142 : } 143 : 144 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnBlockReceived(TransferSession::OutputEvent & event) 145 : { 146 0 : assertChipStackLockedByCurrentThread(); 147 0 : VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE); 148 : 149 0 : ByteSpan blockData(event.blockdata.Data, event.blockdata.Length); 150 0 : return mDelegate->OnTransferData(&mTransferProxy, blockData); 151 : } 152 : 153 0 : void BdxTransferDiagnosticLog::AbortTransferOnFailure(CHIP_ERROR error) 154 : { 155 0 : VerifyOrReturn(CHIP_NO_ERROR != error); 156 0 : LogErrorOnFailure(error); 157 0 : LogErrorOnFailure(mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(error))); 158 : } 159 : 160 0 : void BdxTransferDiagnosticLog::Reset() 161 : { 162 0 : assertChipStackLockedByCurrentThread(); 163 : 164 0 : Responder::ResetTransfer(); 165 : 166 0 : if (mExchangeCtx) 167 : { 168 0 : mIsExchangeClosing = true; 169 0 : mExchangeCtx->Close(); 170 0 : mIsExchangeClosing = false; 171 0 : mExchangeCtx = nullptr; 172 : } 173 : 174 0 : mTransferProxy.Reset(); 175 0 : } 176 : 177 0 : void BdxTransferDiagnosticLog::OnExchangeClosing(Messaging::ExchangeContext * ec) 178 : { 179 : // The exchange can be closing while TransferFacilitator is still accessing us, so 180 : // the BdxTransferDiagnosticLog can not be released "right now". 181 0 : mSystemLayer->ScheduleWork( 182 0 : [](auto * systemLayer, auto * appState) -> void { 183 0 : auto * _this = static_cast<BdxTransferDiagnosticLog *>(appState); 184 0 : _this->mPoolDelegate->Release(_this); 185 0 : }, 186 : this); 187 : 188 : // This block checks and handles the scenario where the exchange is closed externally (e.g., receiving a StatusReport). 189 : // Continuing to use it could lead to a use-after-free error and such an error might occur when the poll timer triggers and 190 : // OnTransferSessionEnd is called. 191 : // We know it's not just us normally closing the exchange if mIsExchangeClosing is false. 192 0 : VerifyOrReturn(!mIsExchangeClosing); 193 0 : mExchangeCtx = nullptr; 194 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL)); 195 : } 196 : 197 : } // namespace bdx 198 : } // namespace chip