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