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 : #include <protocols/bdx/BdxTransferDiagnosticLogPool.h>
22 :
23 : namespace chip {
24 : namespace bdx {
25 :
26 : namespace {
27 : // Max block size for the BDX transfer.
28 : constexpr uint32_t kMaxBdxBlockSize = 1024;
29 :
30 : // How often we poll our transfer session. Sadly, we get allocated on
31 : // unsolicited message, which makes it hard for our clients to configure this.
32 : // But the default poll interval is 500ms, which makes log downloads extremely
33 : // slow.
34 : constexpr System::Clock::Timeout kBdxPollInterval = System::Clock::Milliseconds32(50);
35 :
36 : // Timeout for the BDX transfer session..
37 : constexpr System::Clock::Timeout kBdxTimeout = System::Clock::Seconds16(5 * 60);
38 : constexpr TransferRole kBdxRole = TransferRole::kReceiver;
39 : } // namespace
40 :
41 0 : void BdxTransferDiagnosticLog::HandleTransferSessionOutput(TransferSession::OutputEvent & event)
42 : {
43 0 : assertChipStackLockedByCurrentThread();
44 :
45 0 : ChipLogDetail(BDX, "Got an event %s", event.ToString(event.EventType));
46 :
47 0 : switch (event.EventType)
48 : {
49 0 : case TransferSession::OutputEventType::kInitReceived:
50 0 : AbortTransferOnFailure(OnTransferSessionBegin(event));
51 0 : break;
52 0 : case TransferSession::OutputEventType::kStatusReceived:
53 0 : ChipLogError(BDX, "Got StatusReport %x", static_cast<uint16_t>(event.statusData.statusCode));
54 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL));
55 0 : break;
56 0 : case TransferSession::OutputEventType::kInternalError:
57 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL));
58 0 : break;
59 0 : case TransferSession::OutputEventType::kTransferTimeout:
60 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_TIMEOUT));
61 0 : break;
62 0 : case TransferSession::OutputEventType::kBlockReceived:
63 0 : AbortTransferOnFailure(OnBlockReceived(event));
64 0 : break;
65 0 : case TransferSession::OutputEventType::kMsgToSend:
66 0 : LogErrorOnFailure(OnMessageToSend(event));
67 :
68 0 : if (event.msgTypeData.HasMessageType(MessageType::BlockAckEOF))
69 : {
70 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_NO_ERROR));
71 : }
72 0 : break;
73 0 : case TransferSession::OutputEventType::kAckEOFReceived:
74 : case TransferSession::OutputEventType::kNone:
75 : case TransferSession::OutputEventType::kQueryWithSkipReceived:
76 : case TransferSession::OutputEventType::kQueryReceived:
77 : case TransferSession::OutputEventType::kAckReceived:
78 : case TransferSession::OutputEventType::kAcceptReceived:
79 : // Nothing to do.
80 0 : break;
81 0 : default:
82 : // Should never happen.
83 0 : chipDie();
84 : break;
85 : }
86 0 : }
87 :
88 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
89 : System::PacketBufferHandle && payload)
90 : {
91 0 : assertChipStackLockedByCurrentThread();
92 :
93 0 : VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INCORRECT_STATE);
94 :
95 : // If we receive a SendInit message, then we prepare for transfer
96 0 : if (payloadHeader.HasMessageType(MessageType::SendInit))
97 : {
98 0 : FabricIndex fabricIndex = ec->GetSessionHandle()->GetFabricIndex();
99 0 : NodeId peerNodeId = ec->GetSessionHandle()->GetPeer().GetNodeId();
100 0 : VerifyOrReturnError(fabricIndex != kUndefinedFabricIndex, CHIP_ERROR_INVALID_ARGUMENT);
101 0 : VerifyOrReturnError(peerNodeId != kUndefinedNodeId, CHIP_ERROR_INVALID_ARGUMENT);
102 :
103 0 : mTransferProxy.SetFabricIndex(fabricIndex);
104 0 : mTransferProxy.SetPeerNodeId(peerNodeId);
105 0 : auto flags(TransferControlFlags::kSenderDrive);
106 0 : ReturnLogErrorOnFailure(
107 : Responder::PrepareForTransfer(mSystemLayer, kBdxRole, flags, kMaxBdxBlockSize, kBdxTimeout, kBdxPollInterval));
108 : }
109 :
110 0 : return TransferFacilitator::OnMessageReceived(ec, payloadHeader, std::move(payload));
111 : }
112 :
113 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnMessageToSend(TransferSession::OutputEvent & event)
114 : {
115 0 : assertChipStackLockedByCurrentThread();
116 :
117 0 : VerifyOrReturnError(mExchangeCtx != nullptr, CHIP_ERROR_INCORRECT_STATE);
118 :
119 0 : auto & msgTypeData = event.msgTypeData;
120 0 : bool isStatusReport = msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport);
121 :
122 : // All messages sent from the Sender expect a response, except for a StatusReport which would indicate an error and
123 : // the end of the transfer.
124 0 : Messaging::SendFlags sendFlags;
125 0 : VerifyOrDo(isStatusReport, sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse));
126 :
127 : // If there's an error sending the message, close the exchange by calling Reset.
128 0 : auto err = mExchangeCtx->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(event.MsgData), sendFlags);
129 0 : VerifyOrDo(CHIP_NO_ERROR == err, OnTransferSessionEnd(err));
130 :
131 0 : return err;
132 : }
133 :
134 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnTransferSessionBegin(TransferSession::OutputEvent & event)
135 : {
136 0 : assertChipStackLockedByCurrentThread();
137 0 : VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE);
138 :
139 0 : ReturnErrorOnFailure(mTransferProxy.Init(&mTransfer));
140 0 : return mDelegate->OnTransferBegin(&mTransferProxy);
141 : }
142 :
143 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnTransferSessionEnd(CHIP_ERROR error)
144 : {
145 0 : assertChipStackLockedByCurrentThread();
146 0 : VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE);
147 :
148 0 : LogErrorOnFailure(mDelegate->OnTransferEnd(&mTransferProxy, error));
149 0 : Reset();
150 0 : return CHIP_NO_ERROR;
151 : }
152 :
153 0 : CHIP_ERROR BdxTransferDiagnosticLog::OnBlockReceived(TransferSession::OutputEvent & event)
154 : {
155 0 : assertChipStackLockedByCurrentThread();
156 0 : VerifyOrReturnError(nullptr != mDelegate, CHIP_ERROR_INCORRECT_STATE);
157 :
158 0 : ByteSpan blockData(event.blockdata.Data, event.blockdata.Length);
159 0 : return mDelegate->OnTransferData(&mTransferProxy, blockData);
160 : }
161 :
162 0 : void BdxTransferDiagnosticLog::AbortTransferOnFailure(CHIP_ERROR error)
163 : {
164 0 : VerifyOrReturn(CHIP_NO_ERROR != error);
165 0 : LogErrorOnFailure(error);
166 0 : LogErrorOnFailure(mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(error)));
167 : }
168 :
169 0 : void BdxTransferDiagnosticLog::Reset()
170 : {
171 0 : assertChipStackLockedByCurrentThread();
172 :
173 0 : Responder::ResetTransfer();
174 :
175 0 : if (mExchangeCtx)
176 : {
177 0 : mIsExchangeClosing = true;
178 0 : mExchangeCtx->Close();
179 0 : mIsExchangeClosing = false;
180 0 : mExchangeCtx = nullptr;
181 : }
182 :
183 0 : mTransferProxy.Reset();
184 0 : }
185 :
186 0 : void BdxTransferDiagnosticLog::OnExchangeClosing(Messaging::ExchangeContext * ec)
187 : {
188 : // The exchange can be closing while TransferFacilitator is still accessing us, so
189 : // the BdxTransferDiagnosticLog can not be released "right now".
190 0 : mSystemLayer->ScheduleWork(
191 0 : [](auto * systemLayer, auto * appState) -> void {
192 0 : auto * _this = static_cast<BdxTransferDiagnosticLog *>(appState);
193 0 : _this->mPoolDelegate->Release(_this);
194 0 : },
195 : this);
196 :
197 : // This block checks and handles the scenario where the exchange is closed externally (e.g., receiving a StatusReport).
198 : // Continuing to use it could lead to a use-after-free error and such an error might occur when the poll timer triggers and
199 : // OnTransferSessionEnd is called.
200 : // We know it's not just us normally closing the exchange if mIsExchangeClosing is false.
201 0 : VerifyOrReturn(!mIsExchangeClosing);
202 0 : mExchangeCtx = nullptr;
203 0 : LogErrorOnFailure(OnTransferSessionEnd(CHIP_ERROR_INTERNAL));
204 : }
205 :
206 0 : bool BdxTransferDiagnosticLog::IsForFabric(FabricIndex fabricIndex) const
207 : {
208 0 : if (mExchangeCtx == nullptr || !mExchangeCtx->HasSessionHandle())
209 : {
210 0 : return false;
211 : }
212 :
213 0 : auto session = mExchangeCtx->GetSessionHandle();
214 0 : return session->GetFabricIndex() == fabricIndex;
215 0 : }
216 :
217 0 : void BdxTransferDiagnosticLog::AbortTransfer()
218 : {
219 : // No need to mTransfer.AbortTransfer() here, since that just tries to async
220 : // send a StatusReport to the other side, but we are going away here.
221 0 : Reset();
222 0 : }
223 :
224 : } // namespace bdx
225 : } // namespace chip
|